Import Bento4 to buildroot:how to cover C++ to C interface

4 篇文章 0 订阅
3 篇文章 0 订阅

Due to an unexpected need in the company project, it is required to extract H.264 from an MP4 file. It is recommended to use the open-source tool Bento4.

In most embedded Linux environments, only pure C source code is supported.

About Bento4:
A fast, modern, open source C++ toolkit for all your MP4 and DASH/HLS/CMAF media format needs.
https://www.bento4.com/downloads/

Import Bento4 to buildroot

  1. at buildroot/src/package/Config.in
source "package/bento4/Config.in"
  1. create src/package/bento4 folder
    add:
    2.1 Config.in
config BR2_PACKAGE_BENTO4
	bool "bento4"
	depends on BR2_INSTALL_LIBSTDCPP
	help
	  Bento4 is a C++ class library designed to read and write
	  ISO-MP4 files.
	  https://www.bento4.com/
comment "bento4 support needs a toolchain with C++"
	depends on !BR2_INSTALL_LIBSTDCPP

2.2 bento4.hash

# Locally calculated
sha256  9f3eb912207d7ed9c1e6e05315083404b32a11f8aacd604a9b2bdcb10bf79eb9  bento4-1.6.0-639.tar.gz
sha256  7daae92c8628ada28def8d096fe2fde298b72ec3e2d64a3c408afce38edb361b  Documents/LICENSE.txt

2.3 bento4.mk

################################################################################
#
# bento4
#
################################################################################
BENTO4_VERSION = 1.6.0-639
BENTO4_SITE = $(call github,axiomatic-systems,Bento4,v$(BENTO4_VERSION))
BENTO4_INSTALL_STAGING = YES
BENTO4_LICENSE = GPL-2.0+
BENTO4_LICENSE_FILES = Documents/LICENSE.txt
BENTO4_CPE_ID_VENDOR = axiosys
# Source/C++/Core/Ap4Config.h
ifeq ($(BR2_ENDIAN),"BIG")
BENTO4_BYTE_ORDER = 0
else
BENTO4_BYTE_ORDER = 1
endif
BENTO4_CONF_OPTS += \
	-DBUILD_APPS=OFF \
	-DCMAKE_BUILD_TYPE=Release \
	-DCMAKE_CXX_FLAGS="$(TARGET_CXXFLAGS) -std=c++11 -fPIC -DAP4_PLATFORM_BYTE_ORDER=$(BENTO4_BYTE_ORDER)"
$(eval $(cmake-package))

  1. Add all .patch to src/package/bento4/
    list

Cover C++ interface to C

My plan is to repackage the MP4 to H264/AAC API into a pure C interface. All I need:

headerextract.h
srcMp42Aac_extract.cpp
srcMp42Aac_extract.cpp
MakefileMakefile

Header file
src/Mp4Extract/extract.h

#ifndef __EXTRACT_H__
#define __EXTRACT_H__

#ifdef __cplusplus
extern "C" {
#endif

int AVC_extract_avc(int argc, char **argv);
int AVC_getSampleNum(char *file_name, int *num);
int AVC_getFrameData(char *file_name, int index, char *ptr, int *frame_len);
int AVC_releaseExtractor();

int extract_aac(int argc, char **argv);
int AAC_getSampleNum(char *file_name, int *num);
int AAC_getFrameData(int index, char *ptr, int *frame_len);
int AAC_releaseExtractor();

#ifdef __cplusplus
}
#endif

#endif

src/Mp4Extract/Mp42Aac_extract.cpp

/*****************************************************************
|
|    AP4 - MP4 to AAC File Converter
|
|    Copyright 2002-2008 Axiomatic Systems, LLC
|
|
|    This file is part of Bento4/AP4 (MP4 Atom Processing Library).
|
|    Unless you have obtained Bento4 under a difference license,
|    this version of Bento4 is Bento4|GPL.
|    Bento4|GPL is free software; you can redistribute it and/or modify
|    it under the terms of the GNU General Public License as published by
|    the Free Software Foundation; either version 2, or (at your option)
|    any later version.
|
|    Bento4|GPL is distributed in the hope that it will be useful,
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|    GNU General Public License for more details.
|
|    You should have received a copy of the GNU General Public License
|    along with Bento4|GPL; see the file COPYING.  If not, write to the
|    Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|    02111-1307, USA.
|
****************************************************************/

/*----------------------------------------------------------------------
|   includes
+---------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>

#include "Ap4.h"
#include "extract.h"

/*----------------------------------------------------------------------
|   constants
+---------------------------------------------------------------------*/
#define BANNER                                      \
	"MP4 To AAC File Converter - Version 1.0\n" \
	"(Bento4 Version " AP4_VERSION_STRING ")\n" \
	"(c) 2002-2008 Axiomatic Systems, LLC"

/*----------------------------------------------------------------------
|   PrintUsageAndExit
+---------------------------------------------------------------------*/
static void PrintUsageAndExit()
{
	fprintf(stderr, BANNER "\n\nusage: mp42aac [options] <input> <output>\n"
	                       "  Options:\n"
	                       "  --key <hex>: 128-bit decryption key (in hex: 32 chars)\n");
	exit(1);
}

/*----------------------------------------------------------------------
|   GetSamplingFrequencyIndex
+---------------------------------------------------------------------*/
static unsigned int GetSamplingFrequencyIndex(unsigned int sampling_frequency)
{
	switch (sampling_frequency) {
	case 96000:
		return 0;
	case 88200:
		return 1;
	case 64000:
		return 2;
	case 48000:
		return 3;
	case 44100:
		return 4;
	case 32000:
		return 5;
	case 24000:
		return 6;
	case 22050:
		return 7;
	case 16000:
		return 8;
	case 12000:
		return 9;
	case 11025:
		return 10;
	case 8000:
		return 11;
	case 7350:
		return 12;
	default:
		return 0;
	}
}

/*----------------------------------------------------------------------
|   WriteAdtsHeader
+---------------------------------------------------------------------*/
static AP4_Result WriteAdtsHeader(AP4_ByteStream *output, unsigned int frame_size,
                                  unsigned int sampling_frequency_index, unsigned int channel_configuration)
{
	unsigned char bits[7];

	bits[0] = 0xFF;
	bits[1] = 0xF1; // 0xF9 (MPEG2)
#if (1)
	channel_configuration = 1; // Mono
	bits[2] = 0x40 | (sampling_frequency_index << 2) | (channel_configuration >> 2);
	bits[3] = ((channel_configuration & 0x3) << 6) | ((frame_size + 7) >> 11);
	bits[4] = ((frame_size + 7) >> 3) & 0xFF;
	bits[5] = (((frame_size + 7) << 5) & 0xFF) | 0x1F;
	bits[6] = 0xFC;
#else
	int profile = 2; // AAC LC
	int freqIdx = 11; // 8KHz
	int chanCfg = 1; // mono
	bits[2] = 0x40 | (freqIdx << 2) | (chanCfg >> 2);
	bits[3] = ((chanCfg & 0x3) << 6) | ((frame_size + 7) >> 11);
	bits[4] = ((frame_size + 7) >> 3) & 0xFF;
	bits[5] = (((frame_size + 7) << 5) & 0xFF) | 0x1F;
#endif

	return output->Write(bits, 7);

	/*
		0:  syncword 12 always: '111111111111' 
		12: ID 1 0: MPEG-4, 1: MPEG-2 
		13: layer 2 always: '00' 
		15: protection_absent 1  
		16: profile 2  
		18: sampling_frequency_index 4  
		22: private_bit 1  
		23: channel_configuration 3  
		26: original/copy 1  
		27: home 1  
		28: emphasis 2 only if ID == 0 

		ADTS Variable header: these can change from frame to frame 
		28: copyright_identification_bit 1  
		29: copyright_identification_start 1  
		30: aac_frame_length 13 length of the frame including header (in bytes) 
		43: adts_buffer_fullness 11 0x7FF indicates VBR 
		54: no_raw_data_blocks_in_frame 2  
		ADTS Error check 
		crc_check 16 only if protection_absent == 0 
*/
}

static AP4_Result WriteAdtsHeaderPtr(unsigned int frame_size, unsigned int sampling_frequency_index,
                                     unsigned int channel_configuration, char *ptr)
{
	unsigned char bits[7];

	bits[0] = 0xFF;
	bits[1] = 0xF1; // 0xF9 (MPEG2)
	channel_configuration = 1; // Mono
	bits[2] = 0x40 | (sampling_frequency_index << 2) | (channel_configuration >> 2);
	bits[3] = ((channel_configuration & 0x3) << 6) | ((frame_size + 7) >> 11);
	bits[4] = ((frame_size + 7) >> 3) & 0xFF;
	bits[5] = (((frame_size + 7) << 5) & 0xFF) | 0x1F;
	bits[6] = 0xFC;

	memcpy(ptr, &bits[0], 7);
	return 0;
}

/*----------------------------------------------------------------------
|   DecryptAndWriteSamples
+---------------------------------------------------------------------*/
static void DecryptAndWriteSamples(AP4_Track *track, AP4_SampleDescription *sdesc, AP4_Byte *key,
                                   AP4_ByteStream *output)
{
	AP4_ProtectedSampleDescription *pdesc = AP4_DYNAMIC_CAST(AP4_ProtectedSampleDescription, sdesc);
	if (pdesc == NULL) {
		fprintf(stderr, "ERROR: unable to obtain cipher info\n");
		return;
	}

	AP4_AudioSampleDescription *audio_desc =
	        AP4_DYNAMIC_CAST(AP4_AudioSampleDescription, pdesc->GetOriginalSampleDescription());
	if (audio_desc == NULL) {
		fprintf(stderr, "ERROR: sample description is not audio\n");
		return;
	}
	unsigned int sampling_frequency_index = GetSamplingFrequencyIndex(audio_desc->GetSampleRate());
	unsigned int channel_configuration = audio_desc->GetChannelCount();

	// create the decrypter
	AP4_SampleDecrypter *decrypter = AP4_SampleDecrypter::Create(pdesc, key, 16);
	if (decrypter == NULL) {
		fprintf(stderr, "ERROR: unable to create decrypter\n");
		return;
	}

	AP4_Sample sample;
	AP4_DataBuffer encrypted_data;
	AP4_DataBuffer decrypted_data;
	AP4_Ordinal index = 0;
	AP4_UI32 poolid = 0;
	while (AP4_SUCCEEDED(track->ReadSample(index, sample, encrypted_data))) {
		if (AP4_FAILED(decrypter->DecryptSampleData(poolid, encrypted_data, decrypted_data, NULL))) {
			fprintf(stderr, "ERROR: failed to decrypt sample\n");
			return;
		}

		WriteAdtsHeader(output, decrypted_data.GetDataSize(), sampling_frequency_index, channel_configuration);
		output->Write(decrypted_data.GetData(), decrypted_data.GetDataSize());
		index++;
	}
}

/*----------------------------------------------------------------------
|   WriteSamples
+---------------------------------------------------------------------*/
static void WriteSamples(AP4_Track *track, AP4_SampleDescription *sdesc, AP4_ByteStream *output)
{
	AP4_AudioSampleDescription *audio_desc = AP4_DYNAMIC_CAST(AP4_AudioSampleDescription, sdesc);
	if (audio_desc == NULL) {
		fprintf(stderr, "ERROR: sample description is not audio\n");
		return;
	}
	unsigned int sampling_frequency_index = GetSamplingFrequencyIndex(audio_desc->GetSampleRate());
	unsigned int channel_configuration = audio_desc->GetChannelCount();

	AP4_Sample sample;
	AP4_DataBuffer data;
	AP4_Ordinal index = 0;

	while (AP4_SUCCEEDED(track->ReadSample(index, sample, data))) {
		printf("[%d]freq idx:%d, sample:%d, data;%d\n", index, sampling_frequency_index, sample.GetSize(),
		       data.GetDataSize());
		WriteAdtsHeader(output, sample.GetSize(), sampling_frequency_index, channel_configuration);
		output->Write(data.GetData(), data.GetDataSize());
		index++;
	}
}

static void getSingleSample(AP4_Track *track, AP4_SampleDescription *sdesc, int index, char *ptr, int *frame_len)
{
	AP4_AudioSampleDescription *audio_desc = AP4_DYNAMIC_CAST(AP4_AudioSampleDescription, sdesc);
	if (audio_desc == NULL) {
		fprintf(stderr, "ERROR: sample description is not audio\n");
		return;
	}
	unsigned int sampling_frequency_index = GetSamplingFrequencyIndex(audio_desc->GetSampleRate());
	unsigned int channel_configuration = audio_desc->GetChannelCount();

	AP4_Sample sample;
	AP4_DataBuffer data;
	AP4_Ordinal aacindex = index;

	if (AP4_SUCCEEDED(track->ReadSample(aacindex, sample, data))) {
		WriteAdtsHeaderPtr(sample.GetSize(), sampling_frequency_index, channel_configuration, ptr);
		*frame_len = (int)(data.GetDataSize() + 7);
		memcpy(ptr + 7, (char *)data.GetData(), (int)data.GetDataSize());
	}
}

/*----------------------------------------------------------------------
|   main
+---------------------------------------------------------------------*/
int extract_aac(int argc, char **argv)
{
	int return_value = 1;

	if (argc < 3) {
		PrintUsageAndExit();
	}

	// parse command line
	AP4_Result result;
	char **args = argv + 1;
	unsigned char key[16];
	bool key_option = false;
	if (!strcmp(*args, "--key")) {
		if (argc != 5) {
			fprintf(stderr, "ERROR: invalid command line\n");
			return 1;
		}
		++args;
		if (AP4_ParseHex(*args++, key, 16)) {
			fprintf(stderr, "ERROR: invalid hex format for key\n");
			return 1;
		}
		key_option = true;
	}

	AP4_ByteStream *input = NULL;
	AP4_File *input_file = NULL;
	AP4_ByteStream *output = NULL;
	AP4_Movie *movie = NULL;
	AP4_Track *audio_track = NULL;

	// create the input stream
	result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_READ, input);
	if (AP4_FAILED(result)) {
		fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
		goto end;
	}

	// create the output stream
	result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_WRITE, output);
	if (AP4_FAILED(result)) {
		fprintf(stderr, "ERROR: cannot open output (%d)\n", result);
		goto end;
	}

	// open the file
	input_file = new AP4_File(*input);

	// get the movie
	AP4_SampleDescription *sample_description;
	movie = input_file->GetMovie();
	if (movie == NULL) {
		fprintf(stderr, "ERROR: no movie in file\n");
		goto end;
	}

	// get the audio track
	audio_track = movie->GetTrack(AP4_Track::TYPE_AUDIO);
	if (audio_track == NULL) {
		fprintf(stderr, "ERROR: no audio track found\n");
		goto end;
	}

	// check that the track is of the right type
	sample_description = audio_track->GetSampleDescription(0);
	if (sample_description == NULL) {
		fprintf(stderr, "ERROR: unable to parse sample description\n");
		goto end;
	}

	// show info
	AP4_Debug("Audio Track:\n");
	AP4_Debug("  duration: %u ms\n", (int)audio_track->GetDurationMs());
	AP4_Debug("  sample count: %u\n", (int)audio_track->GetSampleCount());

	switch (sample_description->GetType()) {
	case AP4_SampleDescription::TYPE_MPEG: {
		WriteSamples(audio_track, sample_description, output);
		return_value = 0;
		break;
	}

	case AP4_SampleDescription::TYPE_PROTECTED:
		if (!key_option) {
			fprintf(stderr, "ERROR: encrypted tracks require a key\n");
			return_value = 1;
			break;
		}
		DecryptAndWriteSamples(audio_track, sample_description, key, output);
		result = 0;
		break;

	default:
		fprintf(stderr, "ERROR: unsupported sample type\n");
		return_value = 1;
		break;
	}

end:
	delete input_file;
	if (input)
		input->Release();
	if (output)
		output->Release();

	return return_value;
}

/*reuse global constuctor*/
AP4_ByteStream *g_input = NULL;
AP4_File *g_input_file = NULL;
AP4_Movie *g_movie = NULL;
AP4_Track *g_audio_track = NULL;

int AAC_getSampleNum(char *file_name, int *num)
{
	AP4_Result result;
	int return_value = 1;

	// create the input stream
	result = AP4_FileByteStream::Create(file_name, AP4_FileByteStream::STREAM_MODE_READ, g_input);
	if (AP4_FAILED(result)) {
		fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
		goto end;
	}

	// open the file
	g_input_file = new AP4_File(*g_input);

	g_movie = g_input_file->GetMovie();
	if (g_movie == NULL) {
		fprintf(stderr, "ERROR: no movie in file\n");
		goto end;
	}

	// get the audio track
	g_audio_track = g_movie->GetTrack(AP4_Track::TYPE_AUDIO);
	if (g_audio_track == NULL) {
		fprintf(stderr, "ERROR: no audio track found\n");
		goto end;
	}

	AP4_Debug("  duration: %u ms\n", (int)g_audio_track->GetDurationMs());
	//AP4_Debug("  sample count: %u\n", (int)audio_track->GetSampleCount());

	*num = (int)g_audio_track->GetSampleCount();

end:

	return return_value;
}

int AAC_getFrameData(int index, char *ptr, int *frame_len)
{
	int return_value = 1;
	AP4_SampleDescription *sample_description;
	// check that the track is of the right type
	sample_description = g_audio_track->GetSampleDescription(0);
	if (sample_description == NULL) {
		fprintf(stderr, "ERROR: unable to parse sample description\n");
		goto end;
	}

	getSingleSample(g_audio_track, sample_description, index, ptr, frame_len);
end:
	return return_value;
}

int AAC_releaseExtractor()
{
	delete g_input_file;
	if (g_input)
		g_input->Release();

	return 0;
}

src/Mp4Extract/Mp42Avc_extract.cpp

/*****************************************************************
|
|    AP4 - MP4 to AVC File Converter
|
|    Copyright 2002-2009 Axiomatic Systems, LLC
|
|
|    This file is part of Bento4/AP4 (MP4 Atom Processing Library).
|
|    Unless you have obtained Bento4 under a difference license,
|    this version of Bento4 is Bento4|GPL.
|    Bento4|GPL is free software; you can redistribute it and/or modify
|    it under the terms of the GNU General Public License as published by
|    the Free Software Foundation; either version 2, or (at your option)
|    any later version.
|
|    Bento4|GPL is distributed in the hope that it will be useful,
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|    GNU General Public License for more details.
|
|    You should have received a copy of the GNU General Public License
|    along with Bento4|GPL; see the file COPYING.  If not, write to the
|    Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|    02111-1307, USA.
|
****************************************************************/

/*----------------------------------------------------------------------
|   includes
+---------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>

#include "Ap4.h"
#include "extract.h"

/*----------------------------------------------------------------------
|   constants
+---------------------------------------------------------------------*/
#define BANNER                                      \
	"MP4 To AVC File Converter - Version 1.0\n" \
	"(Bento4 Version " AP4_VERSION_STRING ")\n" \
	"(c) 2002-2009 Axiomatic Systems, LLC"

/*----------------------------------------------------------------------
|   PrintUsageAndExit
+---------------------------------------------------------------------*/
static void PrintUsageAndExit()
{
	fprintf(stderr, BANNER "\n\nusage: mp42avc [options] <input> <output>\n"
	                       "  Options:\n"
	                       "  --key <hex>: 128-bit decryption key (in hex: 32 chars)\n");
	exit(1);
}

/*----------------------------------------------------------------------
|   WriteSample
+---------------------------------------------------------------------*/
static void WriteSample(const AP4_DataBuffer &sample_data, AP4_DataBuffer &prefix, unsigned int nalu_length_size,
                        AP4_ByteStream *output)
{
	const unsigned char *data = sample_data.GetData();
	unsigned int data_size = sample_data.GetDataSize();

	// allocate a buffer for the PES packet
	AP4_DataBuffer frame_data;
	unsigned char *frame_buffer = NULL;

	// add a delimiter if we don't already have one
	bool have_access_unit_delimiter =
	        (data_size > nalu_length_size) &&
	        ((data[nalu_length_size] & 0x1F) == AP4_AVC_NAL_UNIT_TYPE_ACCESS_UNIT_DELIMITER);
	if (!have_access_unit_delimiter) {
		AP4_Size frame_data_size = frame_data.GetDataSize();
		frame_data.SetDataSize(frame_data_size + 6);
		frame_buffer = frame_data.UseData() + frame_data_size;

		// start of access unit
		frame_buffer[0] = 0;
		frame_buffer[1] = 0;
		frame_buffer[2] = 0;
		frame_buffer[3] = 1;
		frame_buffer[4] = 9; // NAL type = Access Unit Delimiter;
		frame_buffer[5] = 0xE0; // Slice types = ANY
	}

	// write the NAL units
	bool prefix_added = false;
	while (data_size) {
		// sanity check
		if (data_size < nalu_length_size)
			break;

		// get the next NAL unit
		AP4_UI32 nalu_size;
		if (nalu_length_size == 1) {
			nalu_size = *data++;
			data_size--;
		} else if (nalu_length_size == 2) {
			nalu_size = AP4_BytesToInt16BE(data);
			data += 2;
			data_size -= 2;
		} else if (nalu_length_size == 4) {
			nalu_size = AP4_BytesToInt32BE(data);
			data += 4;
			data_size -= 4;
		} else {
			break;
		}
		if (nalu_size > data_size)
			break;

		// add the prefix if needed
		if (prefix.GetDataSize() && !prefix_added && !have_access_unit_delimiter) {
			AP4_Size frame_data_size = frame_data.GetDataSize();
			frame_data.SetDataSize(frame_data_size + prefix.GetDataSize());
			frame_buffer = frame_data.UseData() + frame_data_size;
			AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize());
			prefix_added = true;
		}

		// add a start code before the NAL unit
		AP4_Size frame_data_size = frame_data.GetDataSize();
		frame_data.SetDataSize(frame_data_size + 3 + nalu_size);
		frame_buffer = frame_data.UseData() + frame_data_size;
		frame_buffer[0] = 0;
		frame_buffer[1] = 0;
		frame_buffer[2] = 1;
		AP4_CopyMemory(frame_buffer + 3, data, nalu_size);

		// add the prefix if needed
		if (prefix.GetDataSize() && !prefix_added) {
			AP4_Size frame_data_size = frame_data.GetDataSize();
			frame_data.SetDataSize(frame_data_size + prefix.GetDataSize());
			frame_buffer = frame_data.UseData() + frame_data_size;
			AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize());
			prefix_added = true;
		}

		// move to the next NAL unit
		data += nalu_size;
		data_size -= nalu_size;
	}
	output->Write(frame_data.GetData(), frame_data.GetDataSize());
}

void getSample(const AP4_DataBuffer &sample_data, AP4_DataBuffer &prefix, unsigned int nalu_length_size, char *ptr,
               int *len)
{
	const unsigned char *data = sample_data.GetData();
	unsigned int data_size = sample_data.GetDataSize();

	// allocate a buffer for the PES packet
	AP4_DataBuffer frame_data;
	unsigned char *frame_buffer = NULL;

	// add a delimiter if we don't already have one
	bool have_access_unit_delimiter =
	        (data_size > nalu_length_size) &&
	        ((data[nalu_length_size] & 0x1F) == AP4_AVC_NAL_UNIT_TYPE_ACCESS_UNIT_DELIMITER);
	if (!have_access_unit_delimiter) {
		AP4_Size frame_data_size = frame_data.GetDataSize();
		frame_data.SetDataSize(frame_data_size + 6);
		frame_buffer = frame_data.UseData() + frame_data_size;

		// start of access unit
		frame_buffer[0] = 0;
		frame_buffer[1] = 0;
		frame_buffer[2] = 0;
		frame_buffer[3] = 1;
		frame_buffer[4] = 9; // NAL type = Access Unit Delimiter;
		frame_buffer[5] = 0xE0; // Slice types = ANY
	}

	// write the NAL units
	bool prefix_added = false;
	while (data_size) {
		// sanity check
		if (data_size < nalu_length_size)
			break;

		// get the next NAL unit
		AP4_UI32 nalu_size;
		if (nalu_length_size == 1) {
			nalu_size = *data++;
			data_size--;
		} else if (nalu_length_size == 2) {
			nalu_size = AP4_BytesToInt16BE(data);
			data += 2;
			data_size -= 2;
		} else if (nalu_length_size == 4) {
			nalu_size = AP4_BytesToInt32BE(data);
			data += 4;
			data_size -= 4;
		} else {
			break;
		}
		if (nalu_size > data_size)
			break;

		// add the prefix if needed
		if (prefix.GetDataSize() && !prefix_added && !have_access_unit_delimiter) {
			AP4_Size frame_data_size = frame_data.GetDataSize();
			frame_data.SetDataSize(frame_data_size + prefix.GetDataSize());
			frame_buffer = frame_data.UseData() + frame_data_size;
			AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize());
			prefix_added = true;
		}

		// add a start code before the NAL unit
		AP4_Size frame_data_size = frame_data.GetDataSize();
		frame_data.SetDataSize(frame_data_size + 3 + nalu_size);
		frame_buffer = frame_data.UseData() + frame_data_size;
		frame_buffer[0] = 0;
		frame_buffer[1] = 0;
		frame_buffer[2] = 1;
		AP4_CopyMemory(frame_buffer + 3, data, nalu_size);

		// add the prefix if needed
		if (prefix.GetDataSize() && !prefix_added) {
			AP4_Size frame_data_size = frame_data.GetDataSize();
			frame_data.SetDataSize(frame_data_size + prefix.GetDataSize());
			frame_buffer = frame_data.UseData() + frame_data_size;
			AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize());
			prefix_added = true;
		}

		// move to the next NAL unit
		data += nalu_size;
		data_size -= nalu_size;
	}

	//printf("get data size:%d\n", frame_data.GetDataSize());
	memcpy(ptr, (char *)frame_data.GetData(), (int)frame_data.GetDataSize());
	*len = (int)frame_data.GetDataSize();
}

/*----------------------------------------------------------------------
|   MakeFramePrefix
+---------------------------------------------------------------------*/
static AP4_Result MakeFramePrefix(AP4_SampleDescription *sdesc, AP4_DataBuffer &prefix, unsigned int &nalu_length_size)
{
	AP4_AvcSampleDescription *avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, sdesc);
	if (avc_desc == NULL) {
		fprintf(stderr, "ERROR: track does not contain an AVC stream\n");
		return AP4_FAILURE;
	}

	if (sdesc->GetFormat() == AP4_SAMPLE_FORMAT_AVC3 || sdesc->GetFormat() == AP4_SAMPLE_FORMAT_AVC4 ||
	    sdesc->GetFormat() == AP4_SAMPLE_FORMAT_DVAV) {
		// no need for a prefix, SPS/PPS NALs should be in the elementary stream already
		return AP4_SUCCESS;
	}

	// make the SPS/PPS prefix
	nalu_length_size = avc_desc->GetNaluLengthSize();
	for (unsigned int i = 0; i < avc_desc->GetSequenceParameters().ItemCount(); i++) {
		AP4_DataBuffer &buffer = avc_desc->GetSequenceParameters()[i];
		unsigned int prefix_size = prefix.GetDataSize();
		prefix.SetDataSize(prefix_size + 4 + buffer.GetDataSize());
		unsigned char *p = prefix.UseData() + prefix_size;
		*p++ = 0;
		*p++ = 0;
		*p++ = 0;
		*p++ = 1;
		AP4_CopyMemory(p, buffer.GetData(), buffer.GetDataSize());
	}
	for (unsigned int i = 0; i < avc_desc->GetPictureParameters().ItemCount(); i++) {
		AP4_DataBuffer &buffer = avc_desc->GetPictureParameters()[i];
		unsigned int prefix_size = prefix.GetDataSize();
		prefix.SetDataSize(prefix_size + 4 + buffer.GetDataSize());
		unsigned char *p = prefix.UseData() + prefix_size;
		*p++ = 0;
		*p++ = 0;
		*p++ = 0;
		*p++ = 1;
		AP4_CopyMemory(p, buffer.GetData(), buffer.GetDataSize());
	}

	return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   DecryptAndWriteSamples
+---------------------------------------------------------------------*/
static void DecryptAndWriteSamples(AP4_Track *track, AP4_SampleDescription *sdesc, AP4_Byte *key,
                                   AP4_ByteStream *output)
{
	AP4_ProtectedSampleDescription *pdesc = AP4_DYNAMIC_CAST(AP4_ProtectedSampleDescription, sdesc);
	if (pdesc == NULL) {
		fprintf(stderr, "ERROR: unable to obtain cipher info\n");
		return;
	}

	// get the original sample description and make the prefix
	AP4_SampleDescription *orig_sdesc = pdesc->GetOriginalSampleDescription();
	unsigned int nalu_length_size = 0;
	AP4_DataBuffer prefix;
	if (AP4_FAILED(MakeFramePrefix(orig_sdesc, prefix, nalu_length_size))) {
		return;
	}

	// create the decrypter
	AP4_SampleDecrypter *decrypter = AP4_SampleDecrypter::Create(pdesc, key, 16);
	if (decrypter == NULL) {
		fprintf(stderr, "ERROR: unable to create decrypter\n");
		return;
	}

	AP4_Sample sample;
	AP4_DataBuffer encrypted_data;
	AP4_DataBuffer decrypted_data;
	AP4_Ordinal index = 0;
	AP4_UI32 poolid = 0;
	while (AP4_SUCCEEDED(track->ReadSample(index, sample, encrypted_data))) {
		if (AP4_FAILED(decrypter->DecryptSampleData(poolid, encrypted_data, decrypted_data, NULL))) {
			fprintf(stderr, "ERROR: failed to decrypt sample\n");
			return;
		}
		WriteSample(decrypted_data, prefix, nalu_length_size, output);
		index++;
	}
}

/*----------------------------------------------------------------------
|   WriteSamples
+---------------------------------------------------------------------*/
static void WriteSamples(AP4_Track *track, AP4_SampleDescription *sdesc, AP4_ByteStream *output)
{
	// make the frame prefix
	unsigned int nalu_length_size = 0;
	AP4_DataBuffer prefix;
	if (AP4_FAILED(MakeFramePrefix(sdesc, prefix, nalu_length_size))) {
		return;
	}

	AP4_Sample sample;
	AP4_DataBuffer data;
	AP4_Ordinal index = 0;
	while (AP4_SUCCEEDED(track->ReadSample(index, sample, data))) {
		WriteSample(data, prefix, nalu_length_size, output);
		index++;
	}
}

int getSingleFrame(AP4_Track *track, AP4_SampleDescription *sdesc, AP4_Ordinal index, char *ptr, int *frame_len)
{
	// make the frame prefix
	unsigned int nalu_length_size = 0;
	AP4_DataBuffer prefix;
	if (AP4_FAILED(MakeFramePrefix(sdesc, prefix, nalu_length_size))) {
		return -1;
	}

	AP4_Sample sample;
	AP4_DataBuffer data;
	int ret = AP4_SUCCESS;
	ret = track->ReadSample(index, sample, data);

	if (AP4_SUCCEEDED(ret)) {
		getSample(data, prefix, nalu_length_size, ptr, frame_len);
	} else if (AP4_FAILED(ret)) {
		return -1;
	}

	return 0;
}

/*----------------------------------------------------------------------
|   main
+---------------------------------------------------------------------*/
int AVC_extract_avc(int argc, char **argv)
{
	if (argc < 3) {
		PrintUsageAndExit();
	}

	// parse command line
	AP4_Result result;
	char **args = argv + 1;
	unsigned char key[16];
	bool key_option = false;
	if (!strcmp(*args, "--key")) {
		if (argc != 5) {
			fprintf(stderr, "ERROR: invalid command line\n");
			return 1;
		}
		++args;
		if (AP4_ParseHex(*args++, key, 16)) {
			fprintf(stderr, "ERROR: invalid hex format for key\n");
			return 1;
		}
		key_option = true;
	}

	// create the input stream
	AP4_ByteStream *input = NULL;
	result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_READ, input);
	if (AP4_FAILED(result)) {
		fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
	}

	// create the output stream
	AP4_ByteStream *output = NULL;
	result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_WRITE, output);
	if (AP4_FAILED(result)) {
		fprintf(stderr, "ERROR: cannot open output (%d)\n", result);
	}

	// open the file
	AP4_File *input_file = new AP4_File(*input);

	// get the movie
	AP4_SampleDescription *sample_description;
	AP4_Track *video_track;
	AP4_Movie *movie = input_file->GetMovie();
	if (movie == NULL) {
		fprintf(stderr, "ERROR: no movie in file\n");
		goto end;
	}

	// get the video track
	video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO);
	if (video_track == NULL) {
		fprintf(stderr, "ERROR: no video track found\n");
		goto end;
	}

	// check that the track is of the right type
	sample_description = video_track->GetSampleDescription(0);
	if (sample_description == NULL) {
		fprintf(stderr, "ERROR: unable to parse sample description\n");
		goto end;
	}

	// show info
	AP4_Debug("Video Track: type:%d\n", sample_description->GetType());
	AP4_Debug("  duration: %u ms\n", (int)video_track->GetDurationMs());
	AP4_Debug("  sample count: %u\n", (int)video_track->GetSampleCount());

	switch (sample_description->GetType()) {
	case AP4_SampleDescription::TYPE_AVC:
		WriteSamples(video_track, sample_description, output);
		break;

	case AP4_SampleDescription::TYPE_PROTECTED:
		if (!key_option) {
			fprintf(stderr, "ERROR: encrypted tracks require a key\n");
			goto end;
		}
		DecryptAndWriteSamples(video_track, sample_description, key, output);
		break;

	default:
		fprintf(stderr, "ERROR: unsupported sample type\n");
		break;
	}

end:
	delete input_file;
	input->Release();
	output->Release();

	return 0;
}

AP4_ByteStream *g_vinput;
AP4_File *g_vinput_file;
AP4_Movie *g_vmovie;
AP4_Track *gvideo_track;;

int AVC_getSampleNum(char *file_name, int *num)
{
	// create the input stream
	AP4_Result result;
	result = AP4_FileByteStream::Create(file_name, AP4_FileByteStream::STREAM_MODE_READ, g_vinput);
	if (AP4_FAILED(result)) {
		fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
		return -1;
	}

	g_vinput_file = new AP4_File(*g_vinput);
	g_vmovie = g_vinput_file->GetMovie();
	if (g_vmovie == NULL) {
		fprintf(stderr, "ERROR: no movie in file\n");
		return -1;
	}

	// get the video track
	gvideo_track = g_vmovie->GetTrack(AP4_Track::TYPE_VIDEO);
	if (gvideo_track == NULL) {
		fprintf(stderr, "ERROR: no video track found\n");
		return -1;
	}
	AP4_Debug("sample count: %u\n", (int)gvideo_track->GetSampleCount());
	*num = (int)gvideo_track->GetSampleCount();
	return 0;
}

int AVC_getFrameData(char *file_name, int index, char *ptr, int *frame_len)
{
	AP4_Result result;
	int ret;
#if(0)
	// create the input stream
	AP4_ByteStream *input = NULL;
	result = AP4_FileByteStream::Create(file_name, AP4_FileByteStream::STREAM_MODE_READ, input);
	if (AP4_FAILED(result)) {
		fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
	}

	AP4_File *input_file = new AP4_File(*input);
	AP4_Movie *movie = input_file->GetMovie();
	if (movie == NULL) {
		fprintf(stderr, "ERROR: no movie in file\n");
	}

	AP4_Track *video_track;
	
	// get the video track
	video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO);
	if (video_track == NULL) {
		fprintf(stderr, "ERROR: no video track found\n");
	}
#endif
	AP4_SampleDescription *sample_description;
	// check that the track is of the right type
	sample_description = gvideo_track->GetSampleDescription(0);
	if (sample_description == NULL) {
		fprintf(stderr, "ERROR: unable to parse sample description\n");
		return -1;
	}

	ret = getSingleFrame(gvideo_track, sample_description, index, ptr, frame_len);
	if (ret == -1) {
		return -1;
	}

	return 0;
}

int AVC_releaseExtractor()
{
	delete g_vinput_file;
	if (g_vinput)
		g_vinput->Release();

	return 0;
}

Makefile

TARGET =extract_avc
SHARE_LIB_TARGET = libextract.so
MAIN_CXX = main.cpp

CXX = $(CROSS_COMPILE)g++
CC = $(CROSS_COMPILE)gcc
STRIP = $(CROSS_COMPILE)strip
CXXFLAGS = -std=c++11 -g -O2 -fPIC

SRC = Mp42Aac_extract.cpp Mp42Avc_extract.cpp $(BENTO4_LIB)/libap4.a
INC = extract.h
INCS = Adapters Apps CApi Codecs Core Crypto MetaData System
BENTO4_INCS = $(addprefix -I$(BENTO4_INC),$(INCS))

# specify targets
.DEFAULT_GOAL := share

.PHONY: clean $(TARGET) 
$(TARGET):
	$(CXX) -o $(TARGET) $(MAIN_CXX) $(SRC) -I$(BENTO4_INCS)

clean :
	rm -f $(TARGET)* $(SHARE_LIB_TARGET)

.PHONY: share
share:
	$(Q)$(CXX) $(CXXFLAGS) -shared -o $(SHARE_LIB_TARGET) $(SRC) -I$(BENTO4_INCS) -s -DNDEBUG

.PHONY: install uninstall
install:
	cp -f $(SHARE_LIB_TARGET) $(SYSTEM_LIB)
uninstall:
	rm -rf $(SYSTEM_LIB)/$(SHARE_LIB_TARGET)


  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值