Оптимальный метод анализа данных char* в реальном времени, полученных из буфера, для текстового содержимого в C++

Я создал живой непрерывный поток mjpeg. Грубая иллюстрация такая

....[image (jpeg)]->[text "content-length"]->[image (jpeg)]->[text "content-length"]->.... 

Как вы можете видеть, я получаю данные из строки медиаконвейера gstreamer, которая содержит изображение и мой собственный введенный текст (Примечание: хотя я использую Gstreamer, мой вопрос связан только с принципами C++.)

Чтобы проанализировать эти данные в реальном времени, я пытаюсь получить и отправить их в очередь. Впоследствии я планирую анализировать данные для слова «длина содержимого» после того, как очередь содержит определенное количество пакетов.

Мой код выглядит следующим образом:

void clear( std::queue<char> &q )
{
   std::queue<char> empty;
   std::swap( q, empty );
}


static GstFlowReturn new_buffer (GstAppSink *app_sink, gpointer user_data)
{

  GstBuffer* buffer = gst_app_sink_pull_buffer(app_sink);

  //create queue
  std::queue<char> q;

  g_print("The input buffer contents are\n");

  gint i=0;
  for(i=0; buffer->data[i];i++)
  {  
      //g_print("\n%d",i);
      q.push(buffer->data[i]);
  }
  //g_print("\nsize of inbuf is %d\n",GST_BUFFER_SIZE(buffer)); 
  g_print("\n");  
  gst_buffer_unref(buffer);

  //#####################
  //parsing method here???
  //#####################

  clear(q);
  return GST_FLOW_OK;
}

Раньше я использовал циклические очереди/кольцевой буфер в C/C++. Это лучший вариант? Или очереди C++ STL были бы более подходящими в этом сценарии, как указано выше?


person enthusiasticgeek    schedule 02.09.2011    source источник
comment
почему вы ставите content-length после контента? вместо этого вы можете просто префикс каждого кадра (изображения) по его размеру в двоичной форме. почему вы пытаетесь заставить освободить память очереди с помощью функции clear() непосредственно перед тем, как это будет сделано автоматически при выходе из вашего new_buffer()?   -  person Andriy Tylychko    schedule 02.09.2011
comment
Кроме "content-length" у меня есть еще "timestamp". Я не мог найти способ добавить это к метатегам потока. Следовательно, я сжимаю все текстовые данные между двумя кадрами изображения. На самом деле я использую tee от gstreamer, где разветвляется мой медиаконвейер. Один конец тройника - это место, где поток идет на отображение. На другом конце потока я получаю буфер, используя appsink для разбора этого текста. Размещение content-length до или после изображения в формате jpeg не будет проблемой. Но сам jpeg имеет **заголовок**(0xFF, 0xD8) и **нижний колонтитул**(0xFF, 0xD9) Следовательно, я размещаю данные снаружи правильно?   -  person enthusiasticgeek    schedule 02.09.2011
comment
Вы можете разместить любые данные перед фреймом, просто запомните его формат :), так что это может быть content length (4 байта) и timestamp (4 байта). при разборе вашего потока вы просто берете длину контента и временную метку, затем читаете кадр (вы знаете его длину)   -  person Andriy Tylychko    schedule 02.09.2011
comment
Итак, насколько я понимаю: ...[ (content-length) текст ][ (timestamp) текст ][ {0xFF,0xD8} (JPEG Frame) изображение {0xFF,0xD9} ] [ (content-length) текст ] [ (timestamp) текст ][ {0xFF,0xD8} (JPEG-кадр) изображение {0xFF,0xD9} ] [ (content-length) текст ] [ (timestamp) текст ][ {0xFF,0xD8} (JPEG-кадр) изображение {0xFF,0xD9} ]... Это?   -  person enthusiasticgeek    schedule 02.09.2011
comment
нет, [4 байта = длина содержимого][4 байта = метка времени][изображение в формате jpeg], вообще никакого текста, зачем он вам, если вы точно знаете, где ваши данные?   -  person Andriy Tylychko    schedule 02.09.2011
comment
хорошо, я вижу. Итак, вы указываете только необработанные байты char без фактических слов "content-length" и "timestamp"   -  person enthusiasticgeek    schedule 02.09.2011


Ответы (2)


В итоге я использовал класс кольцевого буфера

В заголовочном файле объявить

 //queue size
enum { rb_size = 5 };       // ---->element1 -> element2 -> .... -> elementN -> gap ->
                            // ^                                                     |
                            // |                                                     |  
                            // <--------------------<------------------<-------------V
typedef struct 
{
 char * data[rb_size];
 int head, tail;
} ring_buffer_struct; 

namespace myspace{
class ring_buffer{

    private:

    protected:          

    public: 
    //========= constructor ============
    ring_buffer()  
    {
    //If necessary initialization can happen here.

    }
    //========== destructor =============
    virtual ~ring_buffer()
    {

    }
    //===================================
    virtual void rb_start(ring_buffer_struct *b);
    virtual bool rb_empty(ring_buffer_struct const *b);
    virtual char *  rb_front(ring_buffer_struct const *b);
    virtual char *  rb_rear(ring_buffer_struct const *b);
    virtual void rb_pop_front(ring_buffer_struct *b);
    virtual ring_buffer_struct* rb_push_back(ring_buffer_struct *b);

}; //end of class
}

В файле cpp

//start
void myspace::ring_buffer::rb_start(ring_buffer_struct *b)
{
b->head = 0; b->tail = 0;
}

//clear
bool myspace::ring_buffer::rb_empty(ring_buffer_struct const *b)
{
    return b->head == b->tail;
}

//front element
char *  myspace::ring_buffer::rb_front(ring_buffer_struct const *b)
{
return b->data[b->head]; //data gets popped
}

//rear element
    char *  myspace::ring_buffer::rb_rear(ring_buffer_struct const *b)
{
return b->data[b->tail]; //data gets pushed
}

//pop out front element
void myspace::ring_buffer::rb_pop_front(ring_buffer_struct *b)
{ 

    if(b->head < b->tail)  
{
      ++b->head; 
    }      
    if(b->head > b->tail)
{     
  b->head = 0;        
}

}

//push in rear element
ring_buffer_struct* myspace::ring_buffer::rb_push_back(ring_buffer_struct *b)
{
int new_tail = b->tail;

if (++new_tail >= rb_size)
{   //beginning of the queue
    new_tail = 0;      
}
if (new_tail != b->head)
{             
   //middle of the queue
    b->tail = new_tail;       
}
 if (new_tail <= b->head) 
     {
       b->tail = 0;           
     }

return b;
}

И использовать в main()

  ...

char element1[10] = "abcdefghi";
char element2[10] = "bcdefghij";
char element3[10] = "cdefghijk";

ring_buffer_struct rb;
myspace::ring_buffer q;
q.rb_empty(&rb); //make sure empty
q.rb_start(&rb); //start - initialize

    //initialize
    uint16_t i;
    for(i=0;i<rb_size;i++)
    {
     rb.data[rb.tail] = (char *)"000000000";
     q.rb_push_back(&rb);
    }

    rb.data[rb.tail] = element1;
    q.rb_push_back(&rb);
    q.rb_pop_front(&rb);         //now parse    


    rb.data[rb.tail] = element2;
    q.rb_push_back(&rb);
    q.rb_pop_front(&rb);    //now parse

      ...

Для разбора: я посмотрел этот пост

Простой анализ строк с помощью C++

person enthusiasticgeek    schedule 28.09.2011

Предложение не по теме:

При использовании трюка подкачки для очистки контейнера STL не вызывайте std::swap явно, так как вы можете не получить более оптимизированную версию. Лучший способ:

void clear( std::queue<char> &q )
{
   std::queue<char> empty;
   using std::swap;
   swap( q, empty );
}

Это позволяет компилятору выбрать специализированную версию swap, оптимизированную для типа используемого вами контейнера. Вы также можете попробовать q.swap(empty);, но я не уверен, что все реализации STL предлагают это.

person Adrian McCarthy    schedule 02.09.2011