├── .gitignore ├── LICENSE ├── README.md └── iOS ├── .gitignore ├── cCodes ├── aw_alloc.c ├── aw_alloc.h ├── aw_array.c ├── aw_array.h ├── aw_convert_mp4_to_flv.c ├── aw_convert_mp4_to_flv.h ├── aw_data.c ├── aw_data.h ├── aw_dict.c ├── aw_dict.h ├── aw_encode_flv.c ├── aw_encode_flv.h ├── aw_file.c ├── aw_file.h ├── aw_mp4box.c ├── aw_mp4box.h ├── aw_parse_mp4.c ├── aw_parse_mp4.h ├── aw_pushstream.h ├── aw_rtmp.c ├── aw_rtmp.h ├── aw_utils.c └── aw_utils.h ├── librtmp ├── include │ ├── amf.h │ ├── http.h │ ├── log.h │ └── rtmp.h └── lib │ └── librtmp.a ├── pushStreamInC.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── pushStreamInC ├── AWVideoCapture.h ├── AWVideoCapture.m ├── AWVideoFifoList.h ├── AWVideoFifoList.m ├── AWVideoFlvStream.h ├── AWVideoFlvStream.m ├── AWVideoMp4File.h ├── AWVideoMp4File.m ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m ├── main.m └── test.mov /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | 62 | .DS_Store 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ios_push_stream_with_mp4_hard_encode 2 | iOS push live stream with rtmp protocol. Hardware encode mp4(h264/aac) convert to flv then push to rtmp server 3 | -------------------------------------------------------------------------------- /iOS/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_alloc.c: -------------------------------------------------------------------------------- 1 | // 2 | // aw_alloc.c 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 15/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #include "aw_alloc.h" 10 | #include "aw_utils.h" 11 | #include 12 | #include 13 | 14 | struct aw_debug_alloc_list; 15 | 16 | //分配的所有size 17 | static size_t aw_static_debug_total_alloc_size = 0; 18 | static size_t aw_static_debug_total_free_size = 0; 19 | static struct aw_debug_alloc_list *aw_static_debug_alloc_list = NULL; 20 | static int8_t aw_static_debug_alloc_inited = 0; 21 | 22 | //辅助链表元素 23 | typedef struct aw_debug_alloc_list_element{ 24 | void *pointer; 25 | size_t size; 26 | struct aw_debug_alloc_list_element *pre; 27 | struct aw_debug_alloc_list_element *next; 28 | }aw_debug_alloc_list_element; 29 | 30 | static aw_debug_alloc_list_element * aw_alloc_debug_alloc_list_element(){ 31 | aw_debug_alloc_list_element *element = malloc(sizeof(aw_debug_alloc_list_element)); 32 | memset(element, 0, sizeof(aw_debug_alloc_list_element)); 33 | return element; 34 | } 35 | 36 | static void aw_free_debug_alloc_list_element(aw_debug_alloc_list_element *ele){ 37 | free(ele); 38 | } 39 | 40 | //辅助链表 41 | typedef struct aw_debug_alloc_list{ 42 | aw_debug_alloc_list_element *first; 43 | aw_debug_alloc_list_element *last; 44 | }aw_debug_alloc_list; 45 | 46 | static aw_debug_alloc_list *aw_alloc_debug_alloc_list(){ 47 | aw_debug_alloc_list *list = malloc(sizeof(aw_debug_alloc_list)); 48 | memset(list, 0, sizeof(aw_debug_alloc_list)); 49 | return list; 50 | } 51 | 52 | static void aw_free_debug_alloc_list(aw_debug_alloc_list *list){ 53 | aw_debug_alloc_list_element *ele = list->first; 54 | while (ele) { 55 | aw_debug_alloc_list_element *free_ele = ele; 56 | ele = ele->next; 57 | aw_free_debug_alloc_list_element(free_ele); 58 | } 59 | free(list); 60 | } 61 | 62 | //查找链表元素 63 | static aw_debug_alloc_list_element * aw_find_element_in_debug_alloc_list(aw_debug_alloc_list *list, void * pointer){ 64 | aw_debug_alloc_list_element *ele = list->first; 65 | while (ele) { 66 | if (ele->pointer == pointer) { 67 | return ele; 68 | } 69 | ele = ele->next; 70 | } 71 | return NULL; 72 | } 73 | 74 | //增加元素 75 | static void aw_debug_alloc_list_add_ele(aw_debug_alloc_list *list, void * pointer, size_t size){ 76 | aw_debug_alloc_list_element *find_ele = aw_find_element_in_debug_alloc_list(list, pointer); 77 | if (find_ele) { 78 | AWLog("[ERROR] in debug_alloc_list_add_ele repeat pointer!!!!"); 79 | return; 80 | } 81 | aw_debug_alloc_list_element *ele = aw_alloc_debug_alloc_list_element(); 82 | ele->pointer = pointer; 83 | ele->size = size; 84 | 85 | if (!list->first) { 86 | list->first = ele; 87 | list->last = list->first; 88 | }else{ 89 | list->last->next = ele; 90 | ele->pre = list->last; 91 | list->last = ele; 92 | } 93 | } 94 | 95 | //删除元素 96 | static void aw_debug_alloc_list_remove_ele(aw_debug_alloc_list *list, void * pointer){ 97 | aw_debug_alloc_list_element *find_ele = aw_find_element_in_debug_alloc_list(list, pointer); 98 | if (!find_ele) { 99 | AWLog("[ERROR] in debug_alloc_list_remove_ele cant find pointer!!!!"); 100 | return; 101 | } 102 | 103 | if (find_ele == list->first) { 104 | list->first = find_ele->next; 105 | if (list->first) { 106 | list->first->pre = NULL; 107 | } 108 | }else if(find_ele == list->last){ 109 | list->last = find_ele->pre; 110 | list->last->next = NULL; 111 | }else{ 112 | find_ele->pre->next = find_ele->next; 113 | find_ele->next->pre = find_ele->pre; 114 | } 115 | aw_free_debug_alloc_list_element(find_ele); 116 | } 117 | 118 | //关闭调试 119 | void aw_uninit_debug_alloc(){ 120 | aw_static_debug_total_alloc_size = 0; 121 | aw_static_debug_total_free_size = 0; 122 | if (aw_static_debug_alloc_list) { 123 | aw_free_debug_alloc_list(aw_static_debug_alloc_list); 124 | } 125 | aw_static_debug_alloc_list = NULL; 126 | } 127 | 128 | //开启调试 129 | void aw_init_debug_alloc(){ 130 | if (aw_static_debug_alloc_inited) { 131 | return; 132 | } 133 | aw_static_debug_alloc_inited = 1; 134 | aw_uninit_debug_alloc(); 135 | aw_static_debug_alloc_list = aw_alloc_debug_alloc_list(); 136 | } 137 | 138 | //纪录分配内存 139 | static void aw_debug_alloc_do_alloc(void *pointer, size_t size){ 140 | if (!aw_static_debug_alloc_list) { 141 | return; 142 | } 143 | aw_debug_alloc_list_add_ele(aw_static_debug_alloc_list, pointer, size); 144 | aw_static_debug_total_alloc_size += size; 145 | AWLog(" ---分配了%ld个字节 %p ", size, pointer); 146 | } 147 | 148 | //纪录释放内存 149 | static void aw_debug_alloc_do_free(void *pointer){ 150 | if (!aw_static_debug_alloc_list) { 151 | return; 152 | } 153 | aw_debug_alloc_list_element *free_ele = aw_find_element_in_debug_alloc_list(aw_static_debug_alloc_list, pointer); 154 | if (free_ele) { 155 | aw_static_debug_total_free_size += free_ele->size; 156 | aw_debug_alloc_list_remove_ele(aw_static_debug_alloc_list, pointer); 157 | AWLog(" ---释放了%ld个字节 %p ", free_ele->size, pointer); 158 | }else{ 159 | AWLog("[ERROR] when debug_alloc_do_free cant find pointer(%p) in debug_alloc_list", pointer); 160 | } 161 | } 162 | 163 | //分配 164 | void * aw_alloc(size_t size){ 165 | void *pointer = malloc(size); 166 | memset(pointer, 0, size); 167 | 168 | //纪录分配的指针和大小 169 | aw_debug_alloc_do_alloc(pointer, size); 170 | 171 | return pointer; 172 | } 173 | 174 | //释放 175 | void aw_free(void *p){ 176 | //纪录释放的指针和大小 177 | aw_debug_alloc_do_free(p); 178 | 179 | free(p); 180 | } 181 | 182 | //分配总数 183 | size_t aw_total_alloc_size(){ 184 | return aw_static_debug_total_alloc_size; 185 | } 186 | 187 | //释放总数 188 | size_t aw_total_free_size(){ 189 | return aw_static_debug_total_free_size; 190 | } 191 | 192 | //打印 193 | void aw_print_alloc_description(){ 194 | AWLog("[debug alloc] total alloc size(%ld), total free size(%ld)", aw_total_alloc_size(), aw_total_free_size()); 195 | } -------------------------------------------------------------------------------- /iOS/cCodes/aw_alloc.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_alloc.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 15/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_alloc_h 10 | #define aw_alloc_h 11 | 12 | #include 13 | 14 | //可以监视内存的分配和释放,便于调试内存泄漏 15 | 16 | //自定义 alloc 17 | extern void * aw_alloc(size_t size); 18 | 19 | //自定义 free 20 | extern void aw_free(void *); 21 | 22 | //开启debug alloc 23 | extern void aw_init_debug_alloc(); 24 | 25 | //关闭debug alloc 26 | extern void aw_uninit_debug_alloc(); 27 | 28 | //返回总共alloc的size 29 | extern size_t aw_total_alloc_size(); 30 | 31 | //返回总共free的size 32 | extern size_t aw_total_free_size(); 33 | 34 | //打印内存alloc/free状况 35 | extern void aw_print_alloc_description(); 36 | 37 | #endif /* aw_alloc_h */ 38 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_array.c: -------------------------------------------------------------------------------- 1 | // 2 | // aw_array.c 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 15/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #include "aw_array.h" 10 | #include "aw_alloc.h" 11 | #include 12 | #include "aw_utils.h" 13 | 14 | #define AW_ARRAY_ALLOC_SIZE 100 15 | 16 | extern aw_array_element *copy_aw_array_element(aw_array_element * ele){ 17 | aw_array_element *new_ele = alloc_aw_array_element(); 18 | memcpy(new_ele, ele, sizeof(aw_array_element)); 19 | return new_ele; 20 | } 21 | 22 | extern aw_array_element *alloc_aw_array_element(){ 23 | aw_array_element *array_ele = aw_alloc(sizeof(aw_array_element)); 24 | memset(array_ele, 0, sizeof(aw_array_element)); 25 | return array_ele; 26 | } 27 | 28 | extern void free_aw_array_element(aw_array_element **array_element){ 29 | aw_array_element *ele = *array_element; 30 | switch (ele->type) { 31 | case AW_ARRAY_ELEMENT_TYPE_STRING:{ 32 | if (ele->string_value) { 33 | aw_free(ele->string_value); 34 | } 35 | break; 36 | } 37 | case AW_ARRAY_ELEMENT_TYPE_RELEASE_POINTER: { 38 | if (ele->free_pointer_func) { 39 | ele->free_pointer_func(ele->pointer_value, ele->free_extra); 40 | }else{ 41 | aw_free(ele->pointer_value); 42 | } 43 | break; 44 | } 45 | default: 46 | break; 47 | } 48 | 49 | aw_free(ele); 50 | *array_element = NULL; 51 | } 52 | 53 | extern aw_array *alloc_aw_array(size_t alloc_size){ 54 | aw_array *array = aw_alloc(sizeof(aw_array)); 55 | memset(array, 0, sizeof(aw_array)); 56 | 57 | if (!alloc_size) { 58 | alloc_size = AW_ARRAY_ALLOC_SIZE; 59 | } 60 | 61 | array->array_indexes = aw_alloc(sizeof(aw_array_element *) * alloc_size); 62 | memset(array->array_indexes, 0, sizeof(aw_array_element *) * alloc_size); 63 | array->alloc_size = alloc_size; 64 | array->count = 0; 65 | 66 | return array; 67 | } 68 | 69 | extern void extend_aw_array(aw_array **array, size_t new_size){ 70 | aw_array *now_array = *array; 71 | size_t now_size = now_array->alloc_size; 72 | if (!new_size) { 73 | now_size += AW_ARRAY_ALLOC_SIZE; 74 | }else{ 75 | while (new_size >= now_size) { 76 | now_size += AW_ARRAY_ALLOC_SIZE; 77 | } 78 | } 79 | aw_array *new_array = alloc_aw_array(now_size); 80 | memcpy(new_array->array_indexes, now_array->array_indexes, now_array->alloc_size * sizeof(aw_array_element *)); 81 | new_array->count = now_array->count; 82 | 83 | *array = new_array; 84 | 85 | //此时now_array的数组是浅copy到new_array中,所以释放now_array是不允许释放elements 86 | now_array->count = 0; 87 | free_aw_array(&now_array); 88 | } 89 | 90 | extern int8_t need_extend_aw_array(aw_array *array, int add_count){ 91 | return array->count + add_count > array->alloc_size; 92 | } 93 | 94 | extern void free_aw_array(aw_array **array){ 95 | aw_array *aw_array = *array; 96 | aw_array_element **elements = aw_array->array_indexes; 97 | size_t i = 0; 98 | for (; i < aw_array->count; i++) { 99 | free_aw_array_element(&elements[i]); 100 | } 101 | aw_free(aw_array->array_indexes); 102 | aw_free(aw_array); 103 | *array = NULL; 104 | } 105 | 106 | extern void aw_array_insert_element(aw_array **array, aw_array_element *ele, int index){ 107 | if (!array || !*array || !ele) { 108 | AWLog("[error] in aw_array_insert_element array or ele is NULL"); 109 | return; 110 | } 111 | aw_array *aw_array = *array; 112 | if (need_extend_aw_array(aw_array, 1)) { 113 | extend_aw_array(array, aw_array->alloc_size + 1); 114 | aw_array = *array; 115 | } 116 | if (index >= aw_array->count) { 117 | aw_array->array_indexes[aw_array->count] = ele; 118 | }else { 119 | if(index < 0){ 120 | index = 0; 121 | } 122 | memmove(aw_array->array_indexes + index + 1, aw_array->array_indexes + index, sizeof(aw_array_element *) * (aw_array->count - index)); 123 | aw_array->array_indexes[index] = ele; 124 | } 125 | aw_array->count++; 126 | } 127 | 128 | extern void aw_array_add_element(aw_array **array, aw_array_element *ele){ 129 | if (!array || !*array || !ele) { 130 | AWLog("[error] in aw_array_add_element array or ele is NULL"); 131 | return; 132 | } 133 | aw_array_insert_element(array, ele, (int32_t)(*array)->count); 134 | } 135 | 136 | extern aw_array_element *aw_array_element_at_index(aw_array *array, int index){ 137 | if (!array || index < 0 || index >= array->count) { 138 | AWLog("[error] in aw_array_element_at_index array is NULL or index out of range"); 139 | return NULL; 140 | } 141 | return array->array_indexes[index]; 142 | } 143 | 144 | extern void aw_array_remove_element(aw_array *array, aw_array_element *element){ 145 | aw_array_element_at_index(array, aw_array_index_of_element(array, element)); 146 | } 147 | 148 | extern void aw_array_remove_element_at_index(aw_array *array, int index){ 149 | if (!array || index < 0 || index >= array->count) { 150 | AWLog("[error] in aw_array_remove_element_at_index array is NULL or index out of range"); 151 | return; 152 | } 153 | aw_array_element *will_removed_ele = array->array_indexes[index]; 154 | array->array_indexes[index] = NULL; 155 | if (index < array->count - 1) { 156 | memmove(array->array_indexes + index, array->array_indexes + index + 1, (array->count - 1 - index) * sizeof(aw_array_element *)); 157 | } 158 | 159 | array->count--; 160 | 161 | free_aw_array_element(&will_removed_ele); 162 | } 163 | 164 | extern int32_t aw_array_index_of_element(aw_array *array, aw_array_element *ele){ 165 | if (!array || !ele) { 166 | AWLog("[error] in aw_array_index_of_element array or element is NULL"); 167 | return -1; 168 | } 169 | int i = 0; 170 | for (; i < (int)array->array_indexes; i++) { 171 | if (ele == array->array_indexes[i]) { 172 | return i; 173 | } 174 | } 175 | return -1; 176 | } 177 | 178 | extern aw_array_element *aw_array_add_int(aw_array **array, int32_t i){ 179 | if (!array) { 180 | return NULL; 181 | } 182 | aw_array_element * ele = alloc_aw_array_element(); 183 | ele->type = AW_ARRAY_ELEMENT_TYPE_INT; 184 | ele->int_value = i; 185 | 186 | aw_array_add_element(array, ele); 187 | 188 | return ele; 189 | } 190 | 191 | extern aw_array_element *aw_array_add_double(aw_array **array, double d){ 192 | if (!array) { 193 | return NULL; 194 | } 195 | aw_array_element * ele = alloc_aw_array_element(); 196 | ele->type = AW_ARRAY_ELEMENT_TYPE_DOUBLE; 197 | ele->double_value = d; 198 | 199 | aw_array_add_element(array, ele); 200 | 201 | return ele; 202 | } 203 | 204 | extern aw_array_element *aw_array_add_pointer(aw_array **array, void *pointer){ 205 | if (!array) { 206 | return NULL; 207 | } 208 | aw_array_element * ele = alloc_aw_array_element(); 209 | ele->type = AW_ARRAY_ELEMENT_TYPE_POINTER; 210 | ele->pointer_value = pointer; 211 | 212 | aw_array_add_element(array, ele); 213 | 214 | return ele; 215 | } 216 | 217 | extern aw_array_element *aw_array_add_string(aw_array **array, const char *str){ 218 | if (!array) { 219 | return NULL; 220 | } 221 | aw_array_element * ele = alloc_aw_array_element(); 222 | ele->type = AW_ARRAY_ELEMENT_TYPE_STRING; 223 | 224 | int32_t str_len = (int32_t)strlen(str); 225 | char *real_str = aw_alloc(str_len + 1); 226 | memset(real_str, 0, str_len + 1); 227 | memcpy(real_str, str, str_len); 228 | 229 | ele->string_value = real_str; 230 | 231 | aw_array_add_element(array, ele); 232 | 233 | return ele; 234 | } 235 | 236 | extern aw_array_element *aw_array_add_release_pointer(aw_array **array, void *pointer, void (*free_func)(void *, int), int free_extra){ 237 | if (!array) { 238 | return NULL; 239 | } 240 | aw_array_element * ele = alloc_aw_array_element(); 241 | ele->type = AW_ARRAY_ELEMENT_TYPE_RELEASE_POINTER; 242 | ele->pointer_value = pointer; 243 | ele->free_extra = free_extra; 244 | ele->free_pointer_func = free_func; 245 | 246 | aw_array_add_element(array, ele); 247 | 248 | return ele; 249 | } 250 | 251 | extern void aw_array_swap_element(aw_array *array, int index1, int index2){ 252 | aw_array_element *ele1 = aw_array_element_at_index(array, index1); 253 | if (ele1) { 254 | aw_array_element *ele2 = aw_array_element_at_index(array, index2); 255 | if (ele2) { 256 | aw_array_element *tmp = ele1; 257 | array->array_indexes[index1] = ele2; 258 | array->array_indexes[index2] = tmp; 259 | } 260 | } 261 | } 262 | 263 | extern void aw_array_sort_bubble(aw_array *array, aw_array_sort_policy sort_policy, aw_array_sort_compare_func compare_func){ 264 | int i = 0; 265 | for (; i < array->count; i++) { 266 | int j = i + 1; 267 | for (; j < array->count; j++) { 268 | switch(compare_func(aw_array_element_at_index(array, i), aw_array_element_at_index(array, j))) { 269 | case aw_array_sort_compare_result_great: { 270 | if (sort_policy == aw_array_sort_policy_ascending) { 271 | aw_array_swap_element(array, i, j); 272 | } 273 | break; 274 | } 275 | case aw_array_sort_compare_result_less: { 276 | if (sort_policy == aw_array_sort_policy_descending) { 277 | aw_array_swap_element(array, i, j); 278 | } 279 | break; 280 | } 281 | case aw_array_sort_compare_result_equal: { 282 | // do nothing 283 | break; 284 | } 285 | } 286 | } 287 | } 288 | } 289 | 290 | static void aw_array_sort_quick_inner(aw_array *array, int start, int end, aw_array_sort_policy sort_policy, aw_array_sort_compare_func compare_func){ 291 | int len = end - start + 1; 292 | if (len <= 0) { 293 | return; 294 | } 295 | aw_array_element *key = aw_array_element_at_index(array, start); 296 | 297 | int i = start, j = end; 298 | while (j > i) { 299 | do{ 300 | aw_array_element *end_ele = aw_array_element_at_index(array, j); 301 | aw_array_sort_compare_result compare_result = compare_func(key, end_ele); 302 | if(compare_result == aw_array_sort_compare_result_great && sort_policy == aw_array_sort_policy_ascending) { 303 | aw_array_swap_element(array, i, j); 304 | ++i; 305 | break; 306 | }else if(compare_result == aw_array_sort_compare_result_less && sort_policy == aw_array_sort_policy_descending) { 307 | aw_array_swap_element(array, i, j); 308 | ++i; 309 | break; 310 | } 311 | }while(--j > i); 312 | 313 | if (j <= i) { 314 | break; 315 | } 316 | 317 | do{ 318 | aw_array_element *start_ele = aw_array_element_at_index(array, i); 319 | aw_array_sort_compare_result compare_result = compare_func(start_ele, key); 320 | if(compare_result == aw_array_sort_compare_result_great && sort_policy == aw_array_sort_policy_ascending) { 321 | aw_array_swap_element(array, i, j); 322 | --j; 323 | break; 324 | }else if(compare_result == aw_array_sort_compare_result_less&&sort_policy == aw_array_sort_policy_descending) { 325 | aw_array_swap_element(array, i, j); 326 | --j; 327 | break; 328 | } 329 | }while (++i < j); 330 | } 331 | 332 | aw_array_sort_quick_inner(array, start, i - 1, sort_policy, compare_func); 333 | aw_array_sort_quick_inner(array, i + 1, end, sort_policy, compare_func); 334 | } 335 | 336 | extern void aw_array_sort_quick(aw_array *array, aw_array_sort_policy sort_policy, aw_array_sort_compare_func compare_func){ 337 | aw_array_sort_quick_inner(array, 0, (int32_t)array->count - 1, sort_policy, compare_func); 338 | } 339 | 340 | #include "aw_dict.h" 341 | 342 | static aw_array_sort_compare_result test_aw_array_sort_compare_func(aw_array_element *element1, aw_array_element *element2){ 343 | if (element1->int_value > element2->int_value) { 344 | return aw_array_sort_compare_result_great; 345 | }else if(element1->int_value == element2->int_value){ 346 | return aw_array_sort_compare_result_equal; 347 | }else{ 348 | return aw_array_sort_compare_result_less; 349 | } 350 | } 351 | 352 | static void test_aw_array_sort(aw_array_sort_func sort_func){ 353 | aw_array *array = alloc_aw_array(0); 354 | int i = 0; 355 | int int_arr[10] = {5,6,2,7,8,3,4,1,9,0}; 356 | for (; i < 10; i++) { 357 | int value = int_arr[i]; 358 | aw_array_add_int(&array, value); 359 | } 360 | 361 | AWLog("排序前"); 362 | for (i = 0; i < 10; i++) { 363 | AWLog("%d", aw_array_element_at_index(array, i)->int_value); 364 | } 365 | sort_func(array, aw_array_sort_policy_ascending, test_aw_array_sort_compare_func); 366 | 367 | AWLog("排序后 升序"); 368 | for (i = 0; i < 10; i++) { 369 | AWLog("%d", aw_array_element_at_index(array, i)->int_value); 370 | } 371 | sort_func(array, aw_array_sort_policy_descending, test_aw_array_sort_compare_func); 372 | 373 | AWLog("排序后 降序"); 374 | for (i = 0; i < 10; i++) { 375 | AWLog("%d", aw_array_element_at_index(array, i)->int_value); 376 | } 377 | 378 | free_aw_array(&array); 379 | } 380 | 381 | static void test_release_aw_dict(aw_dict *dict, int extra){ 382 | free_aw_dict(&dict); 383 | } 384 | 385 | extern void test_aw_array(){ 386 | aw_uninit_debug_alloc(); 387 | aw_init_debug_alloc(); 388 | // test insert 389 | aw_array *array = alloc_aw_array(1); 390 | 391 | int i = 0; 392 | for (; i < 10; i++) { 393 | aw_array_add_int(&array, i); 394 | } 395 | 396 | i = 0; 397 | for (; i < array->count; i++) { 398 | AWLog(" - %d", aw_array_element_at_index(array, i)->int_value); 399 | } 400 | 401 | i = 0; 402 | for (; i < 5; i++) { 403 | aw_array_remove_element_at_index(array, i); 404 | } 405 | 406 | i = 0; 407 | for (; i < array->count; i++) { 408 | AWLog(" -- %d", aw_array_element_at_index(array, i)->int_value); 409 | } 410 | 411 | AWLog("test add and remove end"); 412 | 413 | //test string 414 | const char *s = "test string"; 415 | aw_array_add_string(&array, s); 416 | 417 | //test release pointer 418 | aw_dict *dict = alloc_aw_dict(); 419 | i = 0; 420 | for (; i < 10; i++) { 421 | aw_dict_set_int(dict, "x", i, 1); 422 | } 423 | aw_array_add_release_pointer(&array, dict, (void (*)(void *, int))test_release_aw_dict, 0); 424 | 425 | aw_array_add_double(&array, 1.236); 426 | 427 | AWLog(" test 5(%s)", aw_array_element_at_index(array, 5)->string_value); 428 | 429 | aw_dict *get_dict = (aw_dict *)aw_array_element_at_index(array, 6)->pointer_value; 430 | i = 0; 431 | for (; i < 10; i++) { 432 | char xx[5]; 433 | memset(xx, 0, 5); 434 | sprintf(xx, "x.%d", i); 435 | AWLog(" test 6-%d(%d)", i, aw_dict_get_int(get_dict, xx)); 436 | } 437 | AWLog(" test 7(%f)", aw_array_element_at_index(array, 7)->double_value); 438 | 439 | free_aw_array(&array); 440 | 441 | // test_aw_array_sort(aw_array_sort_bubble); 442 | test_aw_array_sort(aw_array_sort_quick); 443 | 444 | aw_print_alloc_description(); 445 | } 446 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_array.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_array.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 15/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_array_h 10 | #define aw_array_h 11 | 12 | #include 13 | 14 | typedef enum aw_array_element_type{ 15 | AW_ARRAY_ELEMENT_TYPE_INT, 16 | AW_ARRAY_ELEMENT_TYPE_DOUBLE, 17 | AW_ARRAY_ELEMENT_TYPE_STRING, 18 | AW_ARRAY_ELEMENT_TYPE_POINTER, 19 | AW_ARRAY_ELEMENT_TYPE_RELEASE_POINTER, 20 | } aw_array_element_type; 21 | 22 | typedef struct aw_array_element{ 23 | aw_array_element_type type; 24 | union{ 25 | int32_t int_value; 26 | double double_value; 27 | void * pointer_value; 28 | char * string_value; 29 | }; 30 | void (*free_pointer_func)(void *, int); 31 | int free_extra; 32 | }aw_array_element; 33 | 34 | extern aw_array_element *copy_aw_array_element(aw_array_element *); 35 | extern aw_array_element *alloc_aw_array_element(); 36 | extern void free_aw_array_element(aw_array_element **); 37 | 38 | typedef struct aw_array{ 39 | aw_array_element **array_indexes; 40 | size_t alloc_size; 41 | size_t count; 42 | }aw_array; 43 | 44 | extern aw_array *alloc_aw_array(size_t); 45 | extern void free_aw_array(aw_array **); 46 | 47 | //基础功能 48 | //插入元素 49 | extern void aw_array_insert_element(aw_array **array, aw_array_element *ele, int index); 50 | //尾部插入元素 51 | extern void aw_array_add_element(aw_array **array, aw_array_element *ele); 52 | //查找元素 53 | extern aw_array_element *aw_array_element_at_index(aw_array *array, int index); 54 | //移除元素 55 | extern void aw_array_remove_element(aw_array *array, aw_array_element *element); 56 | //根据下标移除元素 57 | extern void aw_array_remove_element_at_index(aw_array *array, int index); 58 | //获取指定元素的下标 59 | extern int32_t aw_array_index_of_element(aw_array *array, aw_array_element *ele); 60 | //元素交换 61 | extern void aw_array_swap_element(aw_array *, int, int); 62 | 63 | //具体功能函数 64 | //加入一个int数据 65 | extern aw_array_element *aw_array_add_int(aw_array **, int32_t); 66 | //加入一个double数据 67 | extern aw_array_element *aw_array_add_double(aw_array **, double); 68 | //加入一个无需array管理释放的指针数据 69 | extern aw_array_element *aw_array_add_pointer(aw_array **, void *); 70 | //加入一个字符串数据,会copy一份新的 71 | extern aw_array_element *aw_array_add_string(aw_array **, const char *); 72 | //加入一个需要array管理释放的指针,但是要传入指定类型的释放该指针的方法,如果不传入释放方法,默认使用aw_free释放 73 | extern aw_array_element *aw_array_add_release_pointer(aw_array **, void *, void (*)(void *, int), int); 74 | 75 | //数组排序,实现了冒泡和快速排序 76 | //比较函数结果 77 | typedef enum aw_array_sort_compare_result { 78 | aw_array_sort_compare_result_great = -1, 79 | aw_array_sort_compare_result_less, 80 | aw_array_sort_compare_result_equal, 81 | } aw_array_sort_compare_result; 82 | 83 | //升序or降序排列 84 | typedef enum aw_array_sort_policy{ 85 | aw_array_sort_policy_ascending, 86 | aw_array_sort_policy_descending 87 | }aw_array_sort_policy; 88 | 89 | //排序数组元素比较函数 90 | typedef aw_array_sort_compare_result(*aw_array_sort_compare_func) (aw_array_element* obj1, aw_array_element *obj2); 91 | 92 | //排序函数指针 93 | typedef void(*aw_array_sort_func)(aw_array *, aw_array_sort_policy, aw_array_sort_compare_func); 94 | 95 | //冒泡排序 96 | extern void aw_array_sort_bubble(aw_array *, aw_array_sort_policy, aw_array_sort_compare_func); 97 | 98 | //快速排序 99 | extern void aw_array_sort_quick(aw_array *, aw_array_sort_policy, aw_array_sort_compare_func); 100 | 101 | //测试 102 | extern void test_aw_array(); 103 | 104 | #endif /* aw_array_h */ 105 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_convert_mp4_to_flv.c: -------------------------------------------------------------------------------- 1 | // 2 | // aw_convert_mp4_to_flv.c 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 19/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #include "aw_convert_mp4_to_flv.h" 10 | #include "aw_utils.h" 11 | 12 | #include "aw_rtmp.h" 13 | 14 | static aw_flv_script_tag *aw_create_flv_script_tag(aw_parsed_mp4 *parsed_mp4){ 15 | aw_flv_script_tag *script_tag = alloc_aw_flv_script_tag(); 16 | script_tag->duration = parsed_mp4->duration; 17 | script_tag->width = parsed_mp4->video_width; 18 | script_tag->height = parsed_mp4->video_height; 19 | script_tag->video_data_rate = parsed_mp4->video_data_rate; 20 | script_tag->frame_rate = parsed_mp4->video_frame_rate; 21 | script_tag->v_frame_rate = parsed_mp4->video_frame_rate; 22 | script_tag->a_frame_rate = parsed_mp4->audio_frame_rate; 23 | script_tag->a_sample_rate = parsed_mp4->audio_sample_rate; 24 | script_tag->a_sample_size = parsed_mp4->audio_sample_size; 25 | return script_tag; 26 | } 27 | 28 | static aw_flv_video_tag *aw_create_flv_video_config_record_tag(aw_parsed_mp4 *parsed_mp4){ 29 | aw_flv_video_tag *video_tag = alloc_aw_flv_video_tag(); 30 | video_tag->frame_type = aw_flv_v_frame_type_key; 31 | video_tag->codec_id = aw_flv_v_codec_id_H264; 32 | if (video_tag->codec_id == aw_flv_v_codec_id_H264) { 33 | video_tag->common_tag.header_size = 5; 34 | }else{ 35 | video_tag->common_tag.header_size = 1; 36 | } 37 | video_tag->h264_package_type = aw_flv_v_h264_packet_type_seq_header; 38 | video_tag->h264_composition_time = 0; 39 | video_tag->config_record_data = copy_aw_data(parsed_mp4->video_config_record); 40 | video_tag->common_tag.timestamp = 0; 41 | video_tag->common_tag.data_size = parsed_mp4->video_config_record->size + 11 + video_tag->common_tag.header_size; 42 | return video_tag; 43 | } 44 | 45 | static aw_flv_audio_tag *aw_create_flv_audio_config_record_tag(aw_parsed_mp4 *parsed_mp4){ 46 | aw_flv_audio_tag *audio_tag = alloc_aw_flv_audio_tag(); 47 | audio_tag->sound_format = aw_flv_a_codec_id_AAC; 48 | if (audio_tag->sound_format == aw_flv_a_codec_id_AAC) { 49 | audio_tag->common_tag.header_size = 2; 50 | }else{ 51 | audio_tag->common_tag.header_size = 1; 52 | } 53 | audio_tag->sound_rate = aw_flv_a_sound_rate_44kHZ; 54 | audio_tag->sound_size = aw_flv_a_sound_size_16_bit; 55 | audio_tag->sound_type = parsed_mp4->audio_is_stereo ? aw_flv_a_sound_type_stereo : aw_flv_a_sound_type_mono; 56 | audio_tag->aac_packet_type = aw_flv_a_aac_packge_type_aac_sequence_header; 57 | 58 | audio_tag->config_record_data = copy_aw_data(parsed_mp4->audio_config_record); 59 | audio_tag->common_tag.timestamp = 0; 60 | audio_tag->common_tag.data_size = parsed_mp4->audio_config_record->size + 11 + audio_tag->common_tag.header_size; 61 | return audio_tag; 62 | } 63 | 64 | static aw_flv_video_tag *aw_create_flv_video_tag(aw_parsed_mp4 *parsed_mp4, aw_mp4_av_sample *av_sample){ 65 | aw_flv_video_tag *video_tag = alloc_aw_flv_video_tag(); 66 | video_tag->frame_type = av_sample->is_key_frame ? aw_flv_v_frame_type_key: aw_flv_v_frame_type_inner; 67 | video_tag->codec_id = aw_flv_v_codec_id_H264; 68 | if (video_tag->codec_id == aw_flv_v_codec_id_H264) { 69 | video_tag->common_tag.header_size = 5; 70 | }else{ 71 | video_tag->common_tag.header_size = 1; 72 | } 73 | video_tag->h264_package_type = aw_flv_v_h264_packet_type_nalu; 74 | if (av_sample->is_valid_composite_time) { 75 | video_tag->h264_composition_time = (uint32_t)(av_sample->composite_time * 1000); 76 | }else{ 77 | video_tag->h264_composition_time = 0; 78 | } 79 | video_tag->common_tag.timestamp = (uint32_t)(av_sample->dts * 1000); 80 | video_tag->frame_data = alloc_aw_data(av_sample->sample_size); 81 | memcpy_aw_data(&video_tag->frame_data, parsed_mp4->mp4_file_data->data + av_sample->data_start_in_file, av_sample->sample_size); 82 | video_tag->common_tag.data_size = av_sample->sample_size + 11 + video_tag->common_tag.header_size; 83 | return video_tag; 84 | } 85 | 86 | static aw_flv_audio_tag *aw_create_flv_audio_tag(aw_parsed_mp4 *parsed_mp4, aw_mp4_av_sample *av_sample){ 87 | aw_flv_audio_tag *audio_tag = alloc_aw_flv_audio_tag(); 88 | audio_tag->sound_format = aw_flv_a_codec_id_AAC; 89 | if (audio_tag->sound_format == aw_flv_a_codec_id_AAC) { 90 | audio_tag->common_tag.header_size = 2; 91 | }else{ 92 | audio_tag->common_tag.header_size = 1; 93 | } 94 | audio_tag->sound_rate = aw_flv_a_sound_rate_44kHZ; 95 | audio_tag->sound_size = aw_flv_a_sound_size_16_bit; 96 | audio_tag->sound_type = parsed_mp4->audio_is_stereo ? aw_flv_a_sound_type_stereo : aw_flv_a_sound_type_mono; 97 | audio_tag->aac_packet_type = aw_flv_a_aac_packge_type_aac_raw; 98 | 99 | audio_tag->common_tag.timestamp = (uint32_t)(av_sample->dts * 1000); 100 | audio_tag->frame_data = alloc_aw_data(av_sample->sample_size); 101 | memcpy_aw_data(&audio_tag->frame_data, parsed_mp4->mp4_file_data->data + av_sample->data_start_in_file, av_sample->sample_size); 102 | audio_tag->common_tag.data_size = av_sample->sample_size + 11 + audio_tag->common_tag.header_size; 103 | return audio_tag; 104 | } 105 | 106 | static void aw_write_flv_tag_for_convert_mp4_to_flv(aw_data **flv_data, aw_flv_common_tag *common_tag){ 107 | aw_write_flv_tag(flv_data, common_tag); 108 | switch (common_tag->tag_type) { 109 | case aw_flv_tag_type_audio: { 110 | free_aw_flv_audio_tag(&common_tag->audio_tag); 111 | break; 112 | } 113 | case aw_flv_tag_type_video: { 114 | free_aw_flv_video_tag(&common_tag->video_tag); 115 | break; 116 | } 117 | case aw_flv_tag_type_script: { 118 | free_aw_flv_script_tag(&common_tag->script_tag); 119 | break; 120 | } 121 | } 122 | } 123 | 124 | extern void aw_convert_parsed_mp4_to_flv_data(aw_parsed_mp4 *parsed_mp4, aw_data ** flv_data){ 125 | if (!parsed_mp4) { 126 | AWLog("[error] parsed_mp4 is NULL"); 127 | return; 128 | } 129 | 130 | //初始化flv_data 131 | aw_data *inner_flv_data = *flv_data; 132 | if (!inner_flv_data) { 133 | inner_flv_data = alloc_aw_data(0); 134 | *flv_data = inner_flv_data; 135 | } 136 | 137 | //写入 flv header 138 | aw_write_flv_header(flv_data); 139 | //写入 flv meta data 140 | aw_write_flv_tag_for_convert_mp4_to_flv(flv_data, &aw_create_flv_script_tag(parsed_mp4)->common_tag); 141 | 142 | //写入 video config tag 143 | aw_write_flv_tag_for_convert_mp4_to_flv(flv_data, &aw_create_flv_video_config_record_tag(parsed_mp4)->common_tag); 144 | //写入 audio config tag 145 | aw_write_flv_tag_for_convert_mp4_to_flv(flv_data, &aw_create_flv_audio_config_record_tag(parsed_mp4)->common_tag); 146 | 147 | //写入 samples 148 | int i = 0; 149 | for (; i < parsed_mp4->frames->count; i++) { 150 | aw_mp4_av_sample *av_sample = aw_array_element_at_index(parsed_mp4->frames, i)->pointer_value; 151 | if (av_sample->is_video) { 152 | //写入video tag 153 | aw_write_flv_tag_for_convert_mp4_to_flv(flv_data, &aw_create_flv_video_tag(parsed_mp4, av_sample)->common_tag); 154 | }else{ 155 | //写入audio tag 156 | aw_write_flv_tag_for_convert_mp4_to_flv(flv_data, &aw_create_flv_audio_tag(parsed_mp4, av_sample)->common_tag); 157 | } 158 | } 159 | } 160 | 161 | // ----------- rtmp ----------- 162 | static aw_rtmp_context *static_rtmp_context_for_parsed_mp4 = NULL; 163 | static aw_data *static_rtmp_buff_data = NULL; 164 | 165 | //打开rtmp context 166 | extern int8_t aw_open_rtmp_context_for_parsed_mp4(const char *rtmp_url, aw_rtmp_state_changed_cb state_changed_cb){ 167 | aw_uninit_debug_alloc(); 168 | aw_init_debug_alloc(); 169 | if(!static_rtmp_buff_data){ 170 | static_rtmp_buff_data = alloc_aw_data(0); 171 | } 172 | if (!static_rtmp_context_for_parsed_mp4) { 173 | static_rtmp_context_for_parsed_mp4 = alloc_aw_rtmp_context(rtmp_url, state_changed_cb); 174 | if (static_rtmp_context_for_parsed_mp4) { 175 | return aw_rtmp_open(static_rtmp_context_for_parsed_mp4); 176 | } 177 | }else{ 178 | return aw_rtmp_open(static_rtmp_context_for_parsed_mp4); 179 | } 180 | return 1; 181 | } 182 | 183 | //关闭rtmp context 184 | extern void aw_close_rtmp_context_for_parsed_mp4(){ 185 | if (static_rtmp_context_for_parsed_mp4) { 186 | free_aw_rtmp_context(&static_rtmp_context_for_parsed_mp4); 187 | } 188 | 189 | if(static_rtmp_buff_data){ 190 | free_aw_data(&static_rtmp_buff_data); 191 | } 192 | 193 | aw_print_alloc_description(); 194 | } 195 | 196 | //发送数据 197 | static int aw_convert_mp4_to_flv_stream_send_data(aw_data *data){ 198 | if (!aw_is_rtmp_opened(static_rtmp_context_for_parsed_mp4)) { 199 | return 0; 200 | } 201 | 202 | int write_ret = aw_rtmp_write(static_rtmp_context_for_parsed_mp4, (const char *)data->data, data->size); 203 | 204 | // AWLog("aw_convert_mp4_to_flv_stream_send_data rtmp_write sendbytes=%u ret = %d", data->size, write_ret); 205 | 206 | //发送失败,不丢帧,去掉if就是不管成功失败都丢掉。 207 | if (write_ret) { 208 | reset_aw_data(&data); 209 | } 210 | return write_ret; 211 | } 212 | 213 | //发送当前数据 214 | static int aw_convert_mp4_to_flv_stream_send_curr_data(){ 215 | return aw_convert_mp4_to_flv_stream_send_data(static_rtmp_buff_data); 216 | } 217 | 218 | //发送flv header 219 | static void aw_convert_mp4_to_flv_stream_send_header(aw_parsed_mp4 *parsed_mp4){ 220 | if (static_rtmp_context_for_parsed_mp4->is_header_sent) { 221 | return; 222 | } 223 | 224 | //写入 flv meta data 225 | aw_write_flv_tag_for_convert_mp4_to_flv(&static_rtmp_buff_data, &aw_create_flv_script_tag(parsed_mp4)->common_tag); 226 | if(aw_convert_mp4_to_flv_stream_send_curr_data()){ 227 | 228 | //写入 video config tag 229 | aw_write_flv_tag_for_convert_mp4_to_flv(&static_rtmp_buff_data, &aw_create_flv_video_config_record_tag(parsed_mp4)->common_tag); 230 | if(aw_convert_mp4_to_flv_stream_send_curr_data()){ 231 | 232 | //写入 audio config tag 233 | aw_write_flv_tag_for_convert_mp4_to_flv(&static_rtmp_buff_data, &aw_create_flv_audio_config_record_tag(parsed_mp4)->common_tag); 234 | if(aw_convert_mp4_to_flv_stream_send_curr_data()){ 235 | static_rtmp_context_for_parsed_mp4->is_header_sent = 1; 236 | } 237 | } 238 | } 239 | 240 | reset_aw_data(&static_rtmp_buff_data); 241 | } 242 | 243 | //发送 flv tags 244 | static void aw_convert_parsed_mp4_to_flv_stream_send_body(aw_parsed_mp4 *parsed_mp4){ 245 | if (parsed_mp4->frames->count <= 0) { 246 | return; 247 | } 248 | 249 | static_rtmp_context_for_parsed_mp4->current_time_stamp = static_rtmp_context_for_parsed_mp4->last_time_stamp; 250 | 251 | int i = 0; 252 | for (; i < parsed_mp4->frames->count; i++) { 253 | aw_mp4_av_sample *av_sample = aw_array_element_at_index(parsed_mp4->frames, i)->pointer_value; 254 | av_sample->dts += static_rtmp_context_for_parsed_mp4->current_time_stamp; 255 | //转换数据 256 | if (av_sample->is_video) { 257 | //写入video tag 258 | aw_write_flv_tag_for_convert_mp4_to_flv(&static_rtmp_buff_data, &aw_create_flv_video_tag(parsed_mp4, av_sample)->common_tag); 259 | }else{ 260 | //写入audio tag 261 | aw_write_flv_tag_for_convert_mp4_to_flv(&static_rtmp_buff_data, &aw_create_flv_audio_tag(parsed_mp4, av_sample)->common_tag); 262 | } 263 | 264 | static_rtmp_context_for_parsed_mp4->last_time_stamp = av_sample->dts; 265 | 266 | AWLog("--------send flv tag dts=%f, cts=%f", av_sample->dts, av_sample->composite_time); 267 | 268 | //发送数据 269 | aw_convert_mp4_to_flv_stream_send_curr_data(); 270 | } 271 | } 272 | 273 | //连接成功后,开始发送数据 274 | static void aw_convert_parsed_mp4_to_flv_stream_start(aw_parsed_mp4 *parsed_mp4){ 275 | if (!static_rtmp_context_for_parsed_mp4 || !static_rtmp_buff_data) { 276 | AWLog("[error] aw rtmp url is not open when call stream start"); 277 | return; 278 | } 279 | if (!static_rtmp_context_for_parsed_mp4->is_header_sent) { 280 | aw_convert_mp4_to_flv_stream_send_header(parsed_mp4); 281 | } 282 | 283 | if (static_rtmp_context_for_parsed_mp4->is_header_sent) { 284 | aw_convert_parsed_mp4_to_flv_stream_send_body(parsed_mp4); 285 | } 286 | 287 | free_aw_parsed_mp4(&parsed_mp4); 288 | } 289 | 290 | //对外接口 291 | extern void aw_convert_parsed_mp4_to_flv_stream(const uint8_t *mp4_file_data, size_t len){ 292 | if (!mp4_file_data || len <= 0) { 293 | AWLog("[error] aw_convert_parsed_mp4_to_flv_stream mp4_file_data is null"); 294 | return; 295 | } 296 | //解析 297 | aw_parsed_mp4 *parsed_mp4 = aw_parse_mp4_file_data(mp4_file_data, len); 298 | if (!parsed_mp4) { 299 | AWLog("[error] aw_convert_parsed_mp4_to_flv_stream parsed_mp4 is NULL"); 300 | return; 301 | } 302 | 303 | //开始发送数据 304 | aw_convert_parsed_mp4_to_flv_stream_start(parsed_mp4); 305 | } 306 | 307 | extern void aw_convert_mp4_to_flv_test(const uint8_t *mp4_file_data, size_t len, void (*write_to_file)(aw_data *)){ 308 | aw_uninit_debug_alloc(); 309 | aw_init_debug_alloc(); 310 | 311 | aw_parsed_mp4 *parsed_mp4 = aw_parse_mp4_file_data(mp4_file_data, len); 312 | 313 | aw_data *flv_data = NULL; 314 | aw_convert_parsed_mp4_to_flv_data(parsed_mp4, &flv_data); 315 | 316 | write_to_file(flv_data); 317 | 318 | free_aw_parsed_mp4(&parsed_mp4); 319 | free_aw_data(&flv_data); 320 | 321 | aw_print_alloc_description(); 322 | 323 | AWLog("test convert_mp4_finished"); 324 | } -------------------------------------------------------------------------------- /iOS/cCodes/aw_convert_mp4_to_flv.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_convert_mp4_to_flv.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 19/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_convert_mp4_to_flv_h 10 | #define aw_convert_mp4_to_flv_h 11 | 12 | #include 13 | #include "aw_parse_mp4.h" 14 | #include "aw_encode_flv.h" 15 | #include "aw_rtmp.h" 16 | 17 | //将mp4数据转为flv数据,可以保存为文件 18 | extern void aw_convert_parsed_mp4_to_flv_data(aw_parsed_mp4 *parsed_mp4, aw_data ** flv_data); 19 | 20 | //将mp4数据转为flv数据,并发送至rtmp服务器 21 | //打开rtmp stream 22 | extern int8_t aw_open_rtmp_context_for_parsed_mp4(const char *rtmp_url, aw_rtmp_state_changed_cb state_changed_cb); 23 | //关闭rtmp stream 24 | extern void aw_close_rtmp_context_for_parsed_mp4(); 25 | //将mp4转为flv并发送至flv stream 26 | extern void aw_convert_parsed_mp4_to_flv_stream(const uint8_t *mp4_file_data, size_t len); 27 | 28 | //测试mp4转flv 29 | extern void aw_convert_mp4_to_flv_test(const uint8_t *mp4_file_data, size_t len, void (*write_to_file)(aw_data *)); 30 | 31 | #endif /* aw_convert_mp4_to_flv_h */ 32 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_data.c: -------------------------------------------------------------------------------- 1 | // 2 | // aw_data.c 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 26/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #include "aw_data.h" 10 | #include 11 | #include 12 | #include "aw_utils.h" 13 | 14 | //record size 15 | 16 | static int8_t aw_is_started_record_size = 0; 17 | static uint32_t aw_recorded_size = 0; 18 | 19 | static void aw_start_record_size(){ 20 | if (aw_is_started_record_size) { 21 | AWLog("[ERROR] record size is already in use!!"); 22 | return; 23 | } 24 | aw_is_started_record_size = 1; 25 | aw_recorded_size = 0; 26 | } 27 | 28 | static void aw_increase_record_size(size_t size){ 29 | if (aw_is_started_record_size) { 30 | aw_recorded_size += size; 31 | } 32 | } 33 | 34 | static size_t aw_get_recorded_size(){ 35 | return aw_recorded_size; 36 | } 37 | 38 | static void aw_end_record_size(){ 39 | aw_is_started_record_size = 0; 40 | aw_recorded_size = 0; 41 | } 42 | 43 | #define AW_DATA_ALLOC_BLOCK 10 * 1024 44 | 45 | void memcpy_aw_data(aw_data **tdata, const void *fdata, int size){ 46 | aw_data *tdatap = *tdata; 47 | if (!tdata) { 48 | tdatap = alloc_aw_data(0); 49 | *tdata = tdatap; 50 | } 51 | int new_size = tdatap->alloc_size; 52 | while (new_size < tdatap->size + size) { 53 | new_size += AW_DATA_ALLOC_BLOCK; 54 | } 55 | if (new_size > tdatap->alloc_size) { 56 | extend_aw_data(tdata, new_size); 57 | tdatap = *tdata; 58 | } 59 | 60 | memcpy(tdatap->data + tdatap->curr_pos, fdata, size); 61 | tdatap->curr_pos += size; 62 | tdatap->size = tdatap->curr_pos; 63 | } 64 | 65 | aw_data * alloc_aw_data(int size){ 66 | int awdatasize = sizeof(aw_data); 67 | aw_data *awdata = (aw_data *)aw_alloc(awdatasize); 68 | memset(awdata, 0, awdatasize); 69 | if (size == 0) { 70 | size = AW_DATA_ALLOC_BLOCK; 71 | } 72 | awdata->data = aw_alloc(size); 73 | awdata->alloc_size = size; 74 | return awdata; 75 | } 76 | 77 | extern aw_data * copy_aw_data(aw_data *data){ 78 | if (!data) { 79 | return NULL; 80 | } 81 | aw_data *new_data = alloc_aw_data(data->alloc_size); 82 | memcpy_aw_data(&new_data, data->data, data->size); 83 | return new_data; 84 | } 85 | 86 | void extend_aw_data(aw_data **old_aw_data, int new_size){ 87 | aw_data * new_aw_data = alloc_aw_data(new_size); 88 | memcpy_aw_data(&new_aw_data, (*old_aw_data)->data, (*old_aw_data)->size); 89 | free_aw_data(old_aw_data); 90 | *old_aw_data = new_aw_data; 91 | } 92 | 93 | extern void reset_aw_data(aw_data **data){ 94 | aw_data *aw_data = *data; 95 | if (aw_data) { 96 | aw_data->curr_pos = 0; 97 | memset(aw_data->data, 0, aw_data->size); 98 | aw_data->size = 0; 99 | } 100 | } 101 | 102 | void free_aw_data(aw_data **data){ 103 | aw_data *aw_data = *data; 104 | aw_free(aw_data->data); 105 | aw_data->size = 0; 106 | aw_data->alloc_size = 0; 107 | aw_data->curr_pos = 0; 108 | aw_data->data = 0; 109 | aw_free(aw_data); 110 | *data = NULL; 111 | } 112 | 113 | //大小端转换 114 | #define CONVERT_32(i, store, size) \ 115 | do{\ 116 | store = 0;\ 117 | for(int j = 0; j < size; j++){ \ 118 | store |= (((i & (0xff << j * 8)) >> j * 8) & 0xff) << (((int)size - 1 - j) * 8); \ 119 | } \ 120 | }while(0) 121 | 122 | //大小端不影响移位操作 123 | #define CONVERT_64(i, store, size) \ 124 | do{ \ 125 | /*后面*/ \ 126 | uint32_t i1 = (uint32_t)i; \ 127 | uint32_t store1 = 0; \ 128 | CONVERT_32(i1, store1, 4); \ 129 | uint32_t *ipre = (uint32_t *)&store + 1; \ 130 | memcpy(ipre, &store1, 4); \ 131 | /*前面*/\ 132 | uint32_t i2 = i >> 32; \ 133 | uint32_t store2 = 0; \ 134 | CONVERT_32(i2, store2, 4); \ 135 | uint32_t *itail = (uint32_t *)&store; \ 136 | memcpy(itail, &store2, 4); \ 137 | }while(0) 138 | 139 | #define CONVERT(i, store, size) \ 140 | do{ \ 141 | if(size < 8){ \ 142 | CONVERT_32(i, store, size); \ 143 | } else { \ 144 | CONVERT_64(i, store, size); \ 145 | } \ 146 | }while(0) 147 | 148 | //读取数据 149 | //读取单字节 150 | #define READ_BYTE(type, i) \ 151 | size_t type_size = sizeof(type); \ 152 | memcpy(&i, awdata->data + awdata->curr_pos, type_size); \ 153 | awdata->curr_pos += type_size; \ 154 | aw_increase_record_size(type_size); 155 | 156 | //读取多字节,系统字节序为小端 157 | #define READ_MUTIBYTES_FOR_LITTLE_ENDIAN(i, type, size) \ 158 | do{ \ 159 | READ_MUTIBYTES_FOR_BIG_ENDIAN(i, type, size); \ 160 | type store = 0; \ 161 | CONVERT(i, store, size); \ 162 | i = store; \ 163 | }while(0); 164 | 165 | //读取多字节,系统字节序为大端 166 | #define READ_MUTIBYTES_FOR_BIG_ENDIAN(i, type, isize) \ 167 | do{\ 168 | memcpy(&i, awdata->data + awdata->curr_pos, isize); \ 169 | awdata->curr_pos += isize; \ 170 | aw_increase_record_size(isize); \ 171 | } while(0); 172 | 173 | //读取多字节,自动判断大小端 174 | #define READ_MUTIBYTES(i, type, size) \ 175 | do{\ 176 | if(is_little_endian()) {\ 177 | READ_MUTIBYTES_FOR_LITTLE_ENDIAN(i, type, size); \ 178 | }else{\ 179 | READ_MUTIBYTES_FOR_BIG_ENDIAN(i, type, size); \ 180 | }\ 181 | }while(0); 182 | 183 | //读取多字节带返回值 184 | #define RETURN_FOR_READ_MUTIBYTES(type) \ 185 | type i; \ 186 | READ_MUTIBYTES(i, type, sizeof(type)); \ 187 | return i; 188 | 189 | static uint8_t is_little_endian(){ 190 | union { 191 | int a; 192 | uint8_t b; 193 | }c; 194 | c.a = 1; 195 | return c.b == 1; 196 | } 197 | 198 | static void start_read(aw_data *awdata){ 199 | awdata->curr_pos = 0; 200 | } 201 | 202 | static uint8_t read_uint8(aw_data *awdata){ 203 | uint8_t i; 204 | READ_BYTE(uint8_t, i); 205 | return i; 206 | } 207 | 208 | static uint16_t read_uint16(aw_data *awdata){ 209 | RETURN_FOR_READ_MUTIBYTES(uint16_t); 210 | } 211 | 212 | static uint32_t read_uint24(aw_data *awdata){ 213 | uint32_t i; 214 | READ_MUTIBYTES(i, uint32_t, 3); 215 | return i; 216 | } 217 | 218 | static uint32_t read_uint32(aw_data *awdata){ 219 | RETURN_FOR_READ_MUTIBYTES(uint32_t); 220 | } 221 | 222 | static uint64_t read_uint64(aw_data *awdata){ 223 | RETURN_FOR_READ_MUTIBYTES(uint64_t); 224 | } 225 | 226 | static double read_double(aw_data *awdata){ 227 | uint64_t i; 228 | READ_MUTIBYTES(i, uint64_t, sizeof(i)); 229 | double d = 0; 230 | memcpy(&d, &i, sizeof(i)); 231 | return d; 232 | } 233 | 234 | static void read_string(aw_data* awdata, char **string, int len){ 235 | *string = aw_alloc(len); 236 | memset(*string, 0, len + 1); 237 | memcpy(*string, awdata->data + awdata->curr_pos, len); 238 | awdata->curr_pos += len; 239 | aw_increase_record_size(len); 240 | } 241 | 242 | static void read_bytes(aw_data * awdata, char **bytes, int len){ 243 | *bytes = aw_alloc(len); 244 | memset(*bytes, 0, len); 245 | memcpy(*bytes, awdata->data + awdata->curr_pos, len); 246 | awdata->curr_pos += len; 247 | aw_increase_record_size(len); 248 | } 249 | 250 | static void skip_bytes(aw_data* awdata, int count){ 251 | awdata->curr_pos += count; 252 | aw_increase_record_size(count); 253 | } 254 | 255 | static int remain_count(aw_data *awdata){ 256 | return awdata->size - awdata->curr_pos; 257 | } 258 | 259 | //写入 260 | static void write_uint8(aw_data **awdata, uint8_t v){ 261 | memcpy_aw_data(awdata, &v, 1); 262 | aw_increase_record_size(1); 263 | } 264 | 265 | static void write_uint16(aw_data **awdata, uint16_t v){ 266 | uint16_t big = v; 267 | if (is_little_endian()) { 268 | CONVERT(v, big, sizeof(v)); 269 | } 270 | memcpy_aw_data(awdata, &big, sizeof(uint16_t)); 271 | aw_increase_record_size(2); 272 | } 273 | 274 | static void write_uint24(aw_data **awdata, uint32_t v){ 275 | uint32_t big = v; 276 | if (is_little_endian()) { 277 | CONVERT(v, big, 3); 278 | } 279 | memcpy_aw_data(awdata, &big, sizeof(uint8_t) * 3); 280 | aw_increase_record_size(3); 281 | } 282 | 283 | static void write_uint32(aw_data **awdata, uint32_t v){ 284 | uint32_t big = v; 285 | if (is_little_endian()) { 286 | CONVERT(v, big, sizeof(v)); 287 | } 288 | memcpy_aw_data(awdata, &big, sizeof(uint32_t)); 289 | aw_increase_record_size(4); 290 | } 291 | 292 | static void write_uint64(aw_data **awdata, uint64_t v){ 293 | uint64_t big = v; 294 | if (is_little_endian()) { 295 | CONVERT(v, big, sizeof(v)); 296 | } 297 | memcpy_aw_data(awdata, &big, sizeof(uint64_t)); 298 | aw_increase_record_size(8); 299 | } 300 | 301 | static void write_double(aw_data **awdata, double v){ 302 | uint64_t newV = 0; 303 | memcpy(&newV, &v, sizeof(uint64_t)); 304 | write_uint64(awdata, newV); 305 | } 306 | 307 | static void write_bytes(aw_data **awdata, const uint8_t *bytes, uint32_t count){ 308 | memcpy_aw_data(awdata, bytes, (int)count); 309 | aw_increase_record_size(count); 310 | } 311 | 312 | static void write_string(aw_data **awdata, const char *str, uint32_t len_bytes_count){ 313 | size_t str_len = strlen((const char *)str); 314 | switch (len_bytes_count) { 315 | case 1: 316 | write_uint8(awdata, (uint8_t) str_len); 317 | break; 318 | case 2: 319 | write_uint16(awdata, (uint16_t) str_len); 320 | break; 321 | case 4: 322 | write_uint32(awdata, (uint32_t) str_len); 323 | break; 324 | case 8: 325 | write_uint64(awdata, (uint64_t) str_len); 326 | break; 327 | default: 328 | break; 329 | } 330 | 331 | write_bytes(awdata, (const uint8_t *)str, (int)str_len); 332 | } 333 | 334 | static void write_empty_bytes(aw_data **awdata, uint32_t count){ 335 | uint8_t *empty_bytes = (uint8_t *)aw_alloc(count); 336 | memset(empty_bytes, 0, count); 337 | write_bytes(awdata, empty_bytes, count); 338 | } 339 | 340 | //初始化 awdata operation 341 | aw_data_reader data_reader = { 342 | .start_read = start_read, 343 | .read_uint8 = read_uint8, 344 | .read_uint16 = read_uint16, 345 | .read_uint24 = read_uint24, 346 | .read_uint32 = read_uint32, 347 | .read_uint64 = read_uint64, 348 | .read_double = read_double, 349 | .read_string = read_string, 350 | .read_bytes = read_bytes, 351 | .skip_bytes = skip_bytes, 352 | .remain_count = remain_count, 353 | //debug 354 | .start_record_size = aw_start_record_size, 355 | .record_size = aw_get_recorded_size, 356 | .end_record_size = aw_end_record_size, 357 | }; 358 | 359 | aw_data_writer data_writer = { 360 | .write_uint8 = write_uint8, 361 | .write_uint16 = write_uint16, 362 | .write_uint24 = write_uint24, 363 | .write_uint32 = write_uint32, 364 | .write_uint64 = write_uint64, 365 | .write_double = write_double, 366 | .write_string = write_string, 367 | .write_bytes = write_bytes, 368 | .write_empty_bytes = write_empty_bytes, 369 | //debug 370 | .start_record_size = aw_start_record_size, 371 | .record_size = aw_get_recorded_size, 372 | .end_record_size = aw_end_record_size, 373 | }; 374 | 375 | #define StringMark1(mark) #mark 376 | #define StringMark(mark) StrintMark1(mark) 377 | 378 | #define TWO 2 379 | 380 | #define PRINT(n) AWLog("%s", StringMark(n)) 381 | 382 | #define TEST(type, funcname, value, printMark) \ 383 | wdata = alloc_aw_data(0); \ 384 | type funcname##1 = value; \ 385 | data_writer.write_##funcname(&wdata, funcname##1); \ 386 | data_reader.start_read(wdata); \ 387 | type funcname##2 = data_reader.read_##funcname(wdata); \ 388 | AWLog(#type".1 = %"printMark", u"#type".2 = %"printMark, funcname##1, funcname##2); \ 389 | free_aw_data(&wdata); 390 | 391 | static void aw_data_test_convert(){ 392 | uint8_t u8 = 0x12; 393 | uint8_t u81 = 0; 394 | CONVERT(u8, u81, 1); 395 | AWLog("u8=%x, u81=%x", u8, u81); 396 | 397 | uint16_t u16 = 0x1234; 398 | uint16_t u161 = 0; 399 | CONVERT_32(u16, u161, 2); 400 | AWLog("u16=%x, u161=%x", u16, u161); 401 | 402 | uint32_t u24 = 0x123456; 403 | uint32_t u241 = 0; 404 | CONVERT_32(u24, u241, 3); 405 | AWLog("u24=%x, u241=%x", u24, u241); 406 | 407 | uint32_t u32 = 0x12345678; 408 | uint32_t u321 = 0; 409 | CONVERT_32(u32, u321, 4); 410 | AWLog("u32=%x, u321=%x", u32, u321); 411 | 412 | uint64_t u64 = 0x12345678aabbccdd; 413 | uint64_t u641 = 0; 414 | CONVERT_64(u64, u641, 8); 415 | AWLog("u64=%llx, u641=%llx", u64, u641); 416 | } 417 | 418 | extern void aw_data_test(){ 419 | AWLog("小端 = %d", is_little_endian()); 420 | 421 | aw_data_test_convert(); 422 | aw_data * 423 | // 8字节 424 | TEST(uint8_t, uint8, 0xaa, "x"); 425 | TEST(uint16_t, uint16, 0xaabb, "x"); 426 | TEST(uint32_t, uint24, 0xabcdef, "x"); 427 | TEST(uint32_t, uint32, 0x12345678, "x"); 428 | TEST(uint64_t, uint64, 0x12345678aabbccdd, "llx"); 429 | 430 | 431 | double d1 = 0.12345678; 432 | uint64_t di1 = 0; 433 | memcpy(&di1, &d1, sizeof(uint64_t)); 434 | wdata = alloc_aw_data(0); 435 | data_writer.write_double(&wdata, d1); 436 | data_reader.start_read(wdata); 437 | double d2 = data_reader.read_double(wdata); 438 | uint64_t di2 = 0; 439 | memcpy(&di2, &d2, sizeof(uint64_t)); 440 | AWLog("d.1 = %llx, d.2 = %llx \n", di1, di2); 441 | free_aw_data(&wdata); 442 | } 443 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_data.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_data.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 26/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_data_h 10 | #define aw_data_h 11 | 12 | #include 13 | 14 | //文件数据 15 | typedef struct aw_data{ 16 | uint32_t size;//有效数据长度 17 | uint32_t alloc_size;//分配的数据长度 18 | uint32_t curr_pos;//读取或写入的位置 19 | uint8_t *data;//实际数据 20 | }aw_data; 21 | 22 | //数据操作 23 | //将数据fdata,copy到tdata中,自动扩展tdata大小 24 | extern void memcpy_aw_data(aw_data **tdata, const void *fdata, int size); 25 | //创建 aw_data 传入0表示默认大小10k 26 | extern aw_data * alloc_aw_data(int size); 27 | //copy aw_data 28 | extern aw_data * copy_aw_data(aw_data *data); 29 | //释放aw_data的空间 30 | extern void free_aw_data(aw_data **data); 31 | //给aw_data 扩容/缩小 32 | extern void extend_aw_data(aw_data **old_aw_data, int new_size); 33 | //重置 aw_data,清空数据,一切设为初始状态。 34 | extern void reset_aw_data(aw_data **data); 35 | 36 | //数据读取,自动处理大小端 37 | typedef struct aw_data_reader{ 38 | uint8_t (*read_uint8)(aw_data *); 39 | uint16_t (*read_uint16)(aw_data *); 40 | uint32_t (*read_uint24)(aw_data *); 41 | uint32_t (*read_uint32)(aw_data *); 42 | uint64_t (*read_uint64)(aw_data *); 43 | double (*read_double)(aw_data *); 44 | void (*read_string)(aw_data *, char **, int); 45 | void (*read_bytes)(aw_data *, char **, int); 46 | void (*skip_bytes)(aw_data *, int); 47 | int (*remain_count)(aw_data *); 48 | //debug,纪录某个程序段,一共读取数据的数量。 49 | void (*start_record_size)(); 50 | size_t (*record_size)(); 51 | void (*end_record_size)(); 52 | void (*start_read)(aw_data *); 53 | }aw_data_reader; 54 | 55 | //数据写入,自动处理大小端 56 | typedef struct aw_data_writer{ 57 | void (*write_uint8)(aw_data **, uint8_t); 58 | void (*write_uint16)(aw_data **, uint16_t); 59 | void (*write_uint24)(aw_data **, uint32_t); 60 | void (*write_uint32)(aw_data **, uint32_t); 61 | void (*write_uint64)(aw_data **, uint64_t); 62 | void (*write_double)(aw_data **, double); 63 | void (*write_string)(aw_data **, const char *, uint32_t); 64 | void (*write_bytes)(aw_data **, const uint8_t *, uint32_t); 65 | void (*write_empty_bytes)(aw_data **, uint32_t); 66 | //debug,纪录某个程序段,一共写入数据的数量。 67 | void (*start_record_size)(); 68 | size_t (*record_size)(); 69 | void (*end_record_size)(); 70 | }aw_data_writer; 71 | 72 | //全局读取reader 73 | extern aw_data_reader data_reader; 74 | //全局写入writer 75 | extern aw_data_writer data_writer; 76 | 77 | //测试本文件 78 | extern void aw_data_test(); 79 | 80 | #endif /* aw_data_h */ 81 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_dict.c: -------------------------------------------------------------------------------- 1 | // 2 | // aw_dict.c 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 12/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #include "aw_dict.h" 10 | #include "aw_utils.h" 11 | 12 | extern aw_dict_element *alloc_aw_dict_element(){ 13 | aw_dict_element *ele = (aw_dict_element *)aw_alloc(sizeof(aw_dict_element)); 14 | memset(ele, 0, sizeof(aw_dict_element)); 15 | return ele; 16 | } 17 | 18 | extern void free_aw_dict_element(aw_dict_element **ele, int8_t need_repeat){ 19 | aw_dict_element *ele_inner = *ele; 20 | aw_dict_element *repeat_ele = ele_inner->repeat_next; 21 | if (need_repeat) { 22 | while (repeat_ele) { 23 | aw_dict_element *inner_repeat_ele = repeat_ele; 24 | repeat_ele = repeat_ele->repeat_next; 25 | free_aw_dict_element(&inner_repeat_ele, 0); 26 | } 27 | } 28 | switch (ele_inner->type) { 29 | case AW_DICT_ELEMENT_TYPE_STRING: 30 | aw_free((void *)ele_inner->string_value); 31 | break; 32 | case AW_DICT_ELEMENT_TYPE_RELEASE_POINTER: 33 | if (ele_inner->free_pointer) { 34 | ele_inner->free_pointer(ele_inner->pointer_value, ele_inner->extra); 35 | }else{ 36 | aw_free(ele_inner->pointer_value); 37 | } 38 | break; 39 | default: 40 | break; 41 | } 42 | if (ele_inner->key) { 43 | aw_free((void *)ele_inner->key); 44 | } 45 | aw_free(ele_inner); 46 | *ele = NULL; 47 | } 48 | 49 | extern aw_dict *alloc_aw_dict(){ 50 | aw_dict *dict = (aw_dict *)aw_alloc(sizeof(aw_dict)); 51 | memset(dict, 0, sizeof(aw_dict)); 52 | dict->repeat_seperate_word = '.'; 53 | return dict; 54 | } 55 | 56 | extern void free_aw_dict(aw_dict **dict){ 57 | aw_dict *dictp = *dict; 58 | aw_dict_element *ele = dictp->first; 59 | while (ele) { 60 | dictp->first = ele->next; 61 | free_aw_dict_element(&ele, 1); 62 | ele = dictp->first; 63 | } 64 | aw_free(*dict); 65 | *dict = NULL; 66 | } 67 | 68 | static int is_equal_key(const char *key1, const char *key2){ 69 | return !strcmp(key1, key2); 70 | } 71 | 72 | static int8_t parse_realkey_and_index(const char *key, char **real_key, int *index, char repeat_seprate_word){ 73 | char *p_c = strchr(key, repeat_seprate_word); 74 | if (p_c) { 75 | size_t real_key_size = p_c - key; 76 | char *in_real_key = aw_alloc(real_key_size); 77 | memset(in_real_key, 0, real_key_size); 78 | memcpy(in_real_key, key, real_key_size); 79 | *real_key = in_real_key; 80 | 81 | size_t count_str_size = strlen(key) - real_key_size - 1; 82 | char *count_str = aw_alloc(count_str_size + 1); 83 | memset(count_str, 0, count_str_size + 1); 84 | memcpy(count_str, key + real_key_size + 1, count_str_size); 85 | *index = atoi(count_str); 86 | aw_free(count_str); 87 | 88 | return 1; 89 | }else{ 90 | *real_key = (char *)key; 91 | 92 | return 0; 93 | } 94 | } 95 | 96 | static aw_dict_element * aw_dict_find_ele(aw_dict *dict, const char *key){ 97 | aw_dict_element *ele = dict->first; 98 | if (!ele || !key || !ele->key) { 99 | return NULL; 100 | } 101 | 102 | char *real_key = NULL; 103 | int repeat_count = -1; 104 | int8_t need_free_key = parse_realkey_and_index(key, &real_key, &repeat_count, dict->repeat_seperate_word); 105 | 106 | aw_dict_element *repeat_ele = NULL; 107 | while (ele) { 108 | if (!is_equal_key(real_key, ele->key)) { 109 | ele = ele->next; 110 | }else{ 111 | repeat_ele = ele; 112 | break; 113 | } 114 | } 115 | 116 | if (need_free_key) { 117 | aw_free(real_key); 118 | } 119 | 120 | //查找repeat_next 121 | if (repeat_count >= 0) { 122 | int i = 0; 123 | for (; i < repeat_count; i++) { 124 | if (!repeat_ele) { 125 | return NULL; 126 | } 127 | repeat_ele = repeat_ele->repeat_next; 128 | } 129 | } 130 | 131 | return repeat_ele; 132 | } 133 | 134 | static aw_dict_element *aw_dict_add_new_ele(aw_dict *dict, const char *key, int allow_repeat){ 135 | aw_dict_element *ele = aw_dict_find_ele(dict, key); 136 | 137 | //检查key,不允许带"." 138 | char *p_c = strchr(key, dict->repeat_seperate_word); 139 | if (p_c) { 140 | AWLog("in aw_dict_add_new_ele dont contains '%c' in key(%s)", dict->repeat_seperate_word, key); 141 | return NULL; 142 | } 143 | 144 | if (ele) { 145 | if (allow_repeat) { 146 | aw_dict_element *repeat_ele = ele; 147 | while (repeat_ele->repeat_next) { 148 | repeat_ele = repeat_ele->repeat_next; 149 | } 150 | aw_dict_element *new_repeat_ele = alloc_aw_dict_element(); 151 | repeat_ele->repeat_next = new_repeat_ele; 152 | new_repeat_ele->repeat_pre = repeat_ele; 153 | ele = new_repeat_ele; 154 | }else{ 155 | ele = NULL; 156 | } 157 | }else{ 158 | if (!dict->first) { 159 | dict->first = alloc_aw_dict_element(); 160 | dict->last = dict->first; 161 | ele = dict->first; 162 | }else{ 163 | ele = alloc_aw_dict_element(); 164 | dict->last->next = ele; 165 | ele->pre = dict->last; 166 | dict->last = ele; 167 | } 168 | } 169 | 170 | if (ele) { 171 | int ele_key_len = (int)strlen(key) + 1; 172 | char *ele_key = aw_alloc(ele_key_len); 173 | memset(ele_key, 0, ele_key_len); 174 | memcpy(ele_key, key, ele_key_len - 1); 175 | 176 | ele->key = ele_key; 177 | } 178 | 179 | return ele; 180 | } 181 | 182 | int8_t aw_dict_remove_object(aw_dict *dict, const char *key){ 183 | aw_dict_element *first = dict->first; 184 | if (!first || !key || !first->key) { 185 | return 0; 186 | } 187 | 188 | char *real_key = NULL; 189 | int repeat_count = -1; 190 | int8_t need_free_key = parse_realkey_and_index(key, &real_key, &repeat_count, dict->repeat_seperate_word); 191 | 192 | if (real_key) { 193 | aw_dict_element *ele = aw_dict_find_ele(dict, key); 194 | if (need_free_key) { 195 | aw_free(real_key); 196 | } 197 | if (ele) { 198 | if (repeat_count >= 0) { 199 | //移除的是repeat的element 200 | if (repeat_count == 0) { 201 | //移除repeat结点中的根结点 202 | aw_dict_element *repeat_next = ele->repeat_next; 203 | if (repeat_next) { 204 | if (ele->pre) { 205 | repeat_next->pre = ele->pre; 206 | ele->pre->next = repeat_next; 207 | } 208 | if (ele->next) { 209 | repeat_next->next = ele->next; 210 | ele->next->pre = repeat_next; 211 | } 212 | if (ele == dict->first) { 213 | dict->first = repeat_next; 214 | }else if(ele == dict->last){ 215 | dict->last = repeat_next; 216 | } 217 | }else{ 218 | if (ele == dict->first) { 219 | dict->first = ele->next; 220 | }else if(ele == dict->last){ 221 | dict->last = ele->pre; 222 | }else{ 223 | ele->pre->next = ele->next; 224 | ele->next->pre = ele->pre; 225 | ele->pre = NULL; 226 | ele->next = NULL; 227 | } 228 | } 229 | }else{ 230 | //移除repeat结点中的非根结点 231 | ele->repeat_pre->repeat_next = ele->repeat_next; 232 | ele->repeat_next->repeat_pre = ele->repeat_pre; 233 | ele->pre = NULL; 234 | ele->next = NULL; 235 | } 236 | free_aw_dict_element(&ele, 0); 237 | }else{ 238 | //移除的是主干上的结点 239 | if (ele == dict->first) { 240 | dict->first = ele->next; 241 | }else if(ele == dict->last){ 242 | dict->last = ele->pre; 243 | }else{ 244 | ele->pre->next = ele->next; 245 | ele->next->pre = ele->pre; 246 | ele->pre = NULL; 247 | ele->next = NULL; 248 | } 249 | free_aw_dict_element(&ele, 1); 250 | } 251 | 252 | //检查dict 253 | if(dict->first){ 254 | dict->first->pre = NULL; 255 | if (dict->first == dict->last) { 256 | dict->last = NULL; 257 | } 258 | } 259 | 260 | if (dict->last) { 261 | dict->last->next = NULL; 262 | } 263 | 264 | return 1; 265 | } 266 | } 267 | 268 | return 0; 269 | } 270 | 271 | int8_t aw_dict_set_str(aw_dict *dict, const char *key, const char *str, int allow_repeat){ 272 | aw_dict_element *new_ele = aw_dict_add_new_ele(dict, key, allow_repeat); 273 | 274 | if (new_ele) { 275 | int str_len = (int)strlen(str) + 1; 276 | char *new_str = aw_alloc(str_len); 277 | memcpy(new_str, str, str_len); 278 | new_ele->string_value = new_str; 279 | new_ele->type = AW_DICT_ELEMENT_TYPE_STRING; 280 | return 1; 281 | } 282 | return 0; 283 | } 284 | 285 | const char *aw_dict_get_str(aw_dict *dict, const char *key){ 286 | aw_dict_element *ele = aw_dict_find_ele(dict, key); 287 | if (ele) { 288 | return ele->string_value; 289 | } 290 | return 0; 291 | } 292 | 293 | int8_t aw_dict_set_int(aw_dict *dict, const char *key, int32_t value, int allow_repeat){ 294 | aw_dict_element *new_ele = aw_dict_add_new_ele(dict, key, allow_repeat); 295 | if (new_ele) { 296 | new_ele->int_value = value; 297 | new_ele->type = AW_DICT_ELEMENT_TYPE_INT; 298 | return 1; 299 | } 300 | return 0; 301 | } 302 | 303 | int32_t aw_dict_get_int(aw_dict *dict, const char *key){ 304 | aw_dict_element *ele = aw_dict_find_ele(dict, key); 305 | if (ele) { 306 | return ele->int_value; 307 | } 308 | return 0; 309 | } 310 | 311 | int8_t aw_dict_set_double(aw_dict *dict, const char *key, double value, int allow_repeat){ 312 | aw_dict_element *new_ele = aw_dict_add_new_ele(dict, key, allow_repeat); 313 | if (new_ele) { 314 | new_ele->double_value = value; 315 | new_ele->type = AW_DICT_ELEMENT_TYPE_DOUBLE; 316 | return 1; 317 | } 318 | return 0; 319 | } 320 | 321 | double aw_dict_get_double(aw_dict *dict, const char *key){ 322 | aw_dict_element *ele = aw_dict_find_ele(dict, key); 323 | if (ele) { 324 | return ele->double_value; 325 | } 326 | return 0; 327 | } 328 | 329 | int8_t aw_dict_set_pointer(aw_dict *dict, const char *key, void * value, int allow_repeat){ 330 | aw_dict_element *new_ele = aw_dict_add_new_ele(dict, key, allow_repeat); 331 | if (new_ele) { 332 | new_ele->pointer_value = value; 333 | new_ele->type = AW_DICT_ELEMENT_TYPE_POINTER; 334 | return 1; 335 | } 336 | return 0; 337 | } 338 | 339 | void * aw_dict_get_pointer(aw_dict *dict, const char *key){ 340 | aw_dict_element *ele = aw_dict_find_ele(dict, key); 341 | if (ele) { 342 | return ele->pointer_value; 343 | } 344 | return 0; 345 | } 346 | 347 | int8_t aw_dict_set_release_pointer(aw_dict *dict, const char *key, void * value, void(*release_func)(void *, int), int extra, int allow_repeat){ 348 | aw_dict_element *new_ele = aw_dict_add_new_ele(dict, key, allow_repeat); 349 | if (new_ele) { 350 | new_ele->pointer_value = value; 351 | new_ele->free_pointer = release_func; 352 | new_ele->extra = extra; 353 | new_ele->type = AW_DICT_ELEMENT_TYPE_RELEASE_POINTER; 354 | return 1; 355 | } 356 | return 0; 357 | } 358 | 359 | void * aw_dict_get_release_pointer(aw_dict *dict, const char *key){ 360 | aw_dict_element *ele = aw_dict_find_ele(dict, key); 361 | if (ele) { 362 | return ele->pointer_value; 363 | } 364 | return 0; 365 | } 366 | 367 | const char *aw_dict_description(aw_dict *dict){ 368 | return ""; 369 | } 370 | 371 | //=============test================= 372 | 373 | void aw_dict_test_int(){ 374 | aw_dict *dict = alloc_aw_dict(); 375 | //test int 376 | aw_dict_set_int(dict, "a", 1, 0); 377 | int a = aw_dict_get_int(dict, "a"); 378 | AWLog("a=%d", a); 379 | 380 | //repeat int 381 | aw_dict_set_int(dict, "a", 2, 1); 382 | aw_dict_set_int(dict, "a", 3, 1); 383 | aw_dict_set_int(dict, "a", 4, 1); 384 | aw_dict_set_int(dict, "a", 5, 1); 385 | int a1 = aw_dict_get_int(dict, "a.1"); 386 | int a2 = aw_dict_get_int(dict, "a.2"); 387 | AWLog("a=%d, a1=%d, a2=%d", a, a1, a2); 388 | 389 | //test double 390 | aw_dict_set_double(dict, "a", 6.6, 1); 391 | aw_dict_set_double(dict, "a", 7.7, 1); 392 | 393 | aw_dict_set_double(dict, "b", 8.8, 1); 394 | aw_dict_set_double(dict, "b", 9.9, 1); 395 | aw_dict_set_double(dict, "b", 10.10, 1); 396 | aw_dict_set_double(dict, "b", 11.11, 1); 397 | aw_dict_set_double(dict, "b", 12.12, 1); 398 | aw_dict_set_double(dict, "b", 13.13, 1); 399 | double d1 = aw_dict_get_double(dict, "a.3"); 400 | double d2 = aw_dict_get_double(dict, "a.4"); 401 | double d3 = aw_dict_get_double(dict, "b.0"); 402 | double d4 = aw_dict_get_double(dict, "b.1"); 403 | AWLog("d1=%f, d2=%f, d3=%f, d4=%f", d1, d2, d3, d4); 404 | 405 | aw_dict_remove_object(dict, "a.0"); 406 | aw_dict_remove_object(dict, "a.1"); 407 | 408 | a1 = aw_dict_get_int(dict, "a.1"); 409 | a2 = aw_dict_get_int(dict, "a.2"); 410 | AWLog("---a1=%d, a2=%d", a1, a2); 411 | 412 | aw_dict_remove_object(dict, "b.0"); 413 | aw_dict_remove_object(dict, "b.1"); 414 | aw_dict_remove_object(dict, "b.100"); 415 | 416 | d1 = aw_dict_get_double(dict, "a.3"); 417 | d2 = aw_dict_get_double(dict, "a.4"); 418 | d3 = aw_dict_get_double(dict, "b.0"); 419 | d4 = aw_dict_get_double(dict, "b.1"); 420 | AWLog("d1=%f, d2=%f, d3=%f, d4=%f", d1, d2, d3, d4);//6.6, 7.7, 9.9, 11.11 421 | 422 | aw_dict_remove_object(dict, "a"); 423 | aw_dict_remove_object(dict, "b"); 424 | 425 | 426 | free_aw_dict(&dict); 427 | 428 | AWLog("aw_dict_test int total alloc size = %ld, total free size = %ld", aw_total_alloc_size(), aw_total_free_size()); 429 | aw_uninit_debug_alloc(); 430 | } 431 | 432 | static void aw_dict_test_release_pointer(aw_dict ** entries, int count){ 433 | int i = 0; 434 | for (; i < count; i++) { 435 | aw_dict * dict = entries[i]; 436 | free_aw_dict(&dict); 437 | } 438 | aw_free(entries); 439 | } 440 | 441 | static void aw_dict_test_pointer(){ 442 | aw_uninit_debug_alloc(); 443 | aw_init_debug_alloc(); 444 | 445 | aw_dict *dict = alloc_aw_dict(); 446 | 447 | int count = 100; 448 | 449 | aw_dict **entries = aw_alloc(sizeof(aw_dict *) * count); 450 | 451 | int i = 0; 452 | for (; i < count; i++) { 453 | aw_dict *dict_in = alloc_aw_dict(); 454 | aw_dict_set_int(dict_in, "xx", i, 1); 455 | entries[i] = dict_in; 456 | } 457 | 458 | aw_dict_set_release_pointer(dict, "a", entries, (void (*)(void *, int))aw_dict_test_release_pointer, count, 1); 459 | 460 | free_aw_dict(&dict); 461 | 462 | AWLog("aw_dict_test_pointer int total alloc size = %ld, total free size = %ld", aw_total_alloc_size(), aw_total_free_size()); 463 | 464 | aw_uninit_debug_alloc(); 465 | } 466 | 467 | void aw_dict_test(){ 468 | aw_dict_test_int(); 469 | aw_dict_test_pointer(); 470 | } 471 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_dict.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_dict.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 12/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_dict_h 10 | #define aw_dict_h 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | //字典元素类型 17 | typedef enum aw_dict_element_type{ 18 | AW_DICT_ELEMENT_TYPE_INT, 19 | AW_DICT_ELEMENT_TYPE_DOUBLE, 20 | AW_DICT_ELEMENT_TYPE_STRING, 21 | AW_DICT_ELEMENT_TYPE_POINTER, 22 | AW_DICT_ELEMENT_TYPE_RELEASE_POINTER, 23 | } aw_dict_element_type; 24 | 25 | //字典元素 26 | typedef struct aw_dict_element{ 27 | const char *key; 28 | aw_dict_element_type type; 29 | union{ 30 | int32_t int_value; 31 | double double_value; 32 | void * pointer_value; 33 | char * string_value; 34 | }; 35 | void (*free_pointer)(void *, int); 36 | struct aw_dict_element *next; 37 | struct aw_dict_element *pre; 38 | struct aw_dict_element *repeat_next;//key值重复 39 | struct aw_dict_element *repeat_pre;//key值重复 40 | int extra; 41 | }aw_dict_element; 42 | 43 | extern aw_dict_element *alloc_aw_dict_element(); 44 | extern void free_aw_dict_element(aw_dict_element **, int8_t need_repeat); 45 | 46 | //字典 47 | typedef struct aw_dict{ 48 | aw_dict_element *first; 49 | aw_dict_element *last; 50 | char repeat_seperate_word;//重复key的value,取值分隔符,默认为‘.’,设置为非字母和数字的不常见字符比较合适 51 | }aw_dict; 52 | 53 | extern aw_dict *alloc_aw_dict(); 54 | extern void free_aw_dict(aw_dict **); 55 | 56 | //下列函数中的allow_repeat,表示如果dict中已经存在相同的key如何处理。 57 | //allow_repeat为false,则函数调用失败。 58 | //allow_repeat为true,则会给key绑定2个值。 59 | //可以通过 aw_dict_get_xxx(dict, "key.0"),aw_dict_get_xxx(dict, "key.1"),的方式来区分不同的值。 60 | //也可以通过 aw_dict_remove_object(dict, "key.0"), aw_dict_remove_object(dict, "key.1") 来移除指定的值。 61 | //"key.0","key.1" 中的 "." 事实上是 aw_dict中的 repeat_seprate_word,默认为".",可以根据需要修改此值。 62 | //若改成"@",同一个key对应多个value的访问方式可以这样写:"key@0","key@1" 63 | 64 | //字典存取int 65 | extern int8_t aw_dict_set_int(aw_dict *dict, const char *key, int32_t value, int allow_repeat); 66 | extern int32_t aw_dict_get_int(aw_dict *dict, const char *key); 67 | 68 | //字典存取字符串 69 | extern int8_t aw_dict_set_str(aw_dict *dict, const char *key, const char *v, int allow_repeat); 70 | extern const char *aw_dict_get_str(aw_dict *dict, const char *key); 71 | 72 | //字典存取double 73 | extern int8_t aw_dict_set_double(aw_dict *dict, const char *key, double value, int allow_repeat); 74 | extern double aw_dict_get_double(aw_dict *dict, const char *key); 75 | 76 | //字典存取无需dict管理的指针 77 | extern int8_t aw_dict_set_pointer(aw_dict *dict, const char *key, void * value, int allow_repeat); 78 | extern void * aw_dict_get_pointer(aw_dict *dict, const char *key); 79 | 80 | //字典存取需要dict释放的指针,需要传入释放该指针的release_func,否则会用aw_free释放。 81 | extern int8_t aw_dict_set_release_pointer(aw_dict *dict, const char *key, void * value, void(*release_func)(void *, int), int extra, int allow_repeat); 82 | extern void * aw_dict_get_release_pointer(aw_dict *dict, const char *key); 83 | 84 | //字典中移除某个key 85 | extern int8_t aw_dict_remove_object(aw_dict *dict, const char *key); 86 | 87 | extern const char *aw_dict_description(aw_dict *dict); 88 | 89 | extern void aw_dict_test(); 90 | 91 | #endif /* aw_dict_h */ 92 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_encode_flv.c: -------------------------------------------------------------------------------- 1 | // 2 | // aw_encode_flv.c 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 19/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #include "aw_encode_flv.h" 10 | #include "aw_alloc.h" 11 | #include 12 | #include "aw_utils.h" 13 | 14 | extern aw_flv_script_tag *alloc_aw_flv_script_tag(){ 15 | aw_flv_script_tag *script_tag = aw_alloc(sizeof(aw_flv_script_tag)); 16 | memset(script_tag, 0, sizeof(aw_flv_script_tag)); 17 | 18 | //初始化 19 | script_tag->common_tag.script_tag = script_tag; 20 | script_tag->common_tag.tag_type = aw_flv_tag_type_script; 21 | 22 | script_tag->v_codec_id = aw_flv_v_codec_id_H264; 23 | script_tag->a_codec_id = aw_flv_a_codec_id_AAC; 24 | 25 | //经计算,我们写入的script 的body size为255 26 | script_tag->common_tag.data_size = 255; 27 | 28 | return script_tag; 29 | } 30 | 31 | extern void free_aw_flv_script_tag(aw_flv_script_tag ** script_tag){ 32 | aw_free(*script_tag); 33 | *script_tag = NULL; 34 | } 35 | 36 | extern aw_flv_audio_tag *alloc_aw_flv_audio_tag(){ 37 | aw_flv_audio_tag *audio_tag = aw_alloc(sizeof(aw_flv_audio_tag)); 38 | memset(audio_tag, 0, sizeof(aw_flv_audio_tag)); 39 | //初始化 40 | audio_tag->common_tag.audio_tag = audio_tag; 41 | audio_tag->common_tag.tag_type = aw_flv_tag_type_audio; 42 | 43 | return audio_tag; 44 | } 45 | 46 | extern void free_aw_flv_audio_tag(aw_flv_audio_tag ** audio_tag){ 47 | if ((*audio_tag)->config_record_data) { 48 | free_aw_data(&(*audio_tag)->config_record_data); 49 | } 50 | if((*audio_tag)->frame_data){ 51 | free_aw_data(&(*audio_tag)->frame_data); 52 | } 53 | aw_free(*audio_tag); 54 | *audio_tag = NULL; 55 | } 56 | 57 | extern aw_flv_video_tag *alloc_aw_flv_video_tag(){ 58 | aw_flv_video_tag *video_tag = aw_alloc(sizeof(aw_flv_video_tag)); 59 | memset(video_tag, 0, sizeof(aw_flv_video_tag)); 60 | //初始化 61 | video_tag->common_tag.video_tag = video_tag; 62 | video_tag->common_tag.tag_type = aw_flv_tag_type_video; 63 | 64 | return video_tag; 65 | } 66 | 67 | extern void free_aw_flv_video_tag(aw_flv_video_tag **video_tag){ 68 | if ((*video_tag)->config_record_data) { 69 | free_aw_data(&(*video_tag)->config_record_data); 70 | } 71 | if((*video_tag)->frame_data){ 72 | free_aw_data(&(*video_tag)->frame_data); 73 | } 74 | aw_free(*video_tag); 75 | *video_tag = NULL; 76 | } 77 | 78 | static void aw_write_tag_header(aw_data **flv_data, aw_flv_common_tag *common_tag){ 79 | data_writer.write_uint8(flv_data, common_tag->tag_type); 80 | data_writer.write_uint24(flv_data, common_tag->data_size - 11); 81 | data_writer.write_uint24(flv_data, common_tag->timestamp); 82 | data_writer.write_uint8(flv_data, common_tag->timestamp_extend); 83 | data_writer.write_uint24(flv_data, common_tag->stream_id); 84 | } 85 | 86 | static void aw_write_audio_tag_body(aw_data **flv_data, aw_flv_audio_tag *audio_tag){ 87 | uint8_t audio_header = 0; 88 | audio_header |= audio_tag->sound_format << 4 & 0xf0; 89 | audio_header |= audio_tag->sound_rate << 2 & 0xc; 90 | audio_header |= audio_tag->sound_size << 1 & 0x2; 91 | audio_header |= audio_tag->sound_type &0x1; 92 | data_writer.write_uint8(flv_data, audio_header); 93 | 94 | if (audio_tag->sound_format == aw_flv_a_codec_id_AAC) { 95 | data_writer.write_uint8(flv_data, audio_tag->aac_packet_type); 96 | } 97 | switch (audio_tag->aac_packet_type) { 98 | case aw_flv_a_aac_packge_type_aac_sequence_header: { 99 | data_writer.write_bytes(flv_data, audio_tag->config_record_data->data, audio_tag->config_record_data->size); 100 | break; 101 | } 102 | case aw_flv_a_aac_packge_type_aac_raw: { 103 | data_writer.write_bytes(flv_data, audio_tag->frame_data->data, audio_tag->frame_data->size); 104 | break; 105 | } 106 | } 107 | } 108 | 109 | static void aw_write_video_tag_body(aw_data **flv_data, aw_flv_video_tag *video_tag){ 110 | uint8_t video_header = 0; 111 | video_header |= video_tag->frame_type << 4 & 0xf0; 112 | video_header |= video_tag->codec_id; 113 | data_writer.write_uint8(flv_data, video_header); 114 | 115 | if (video_tag->codec_id == aw_flv_v_codec_id_H264) { 116 | data_writer.write_uint8(flv_data, video_tag->h264_package_type); 117 | data_writer.write_uint24(flv_data, video_tag->h264_composition_time); 118 | } 119 | 120 | switch (video_tag->h264_package_type) { 121 | case aw_flv_v_h264_packet_type_seq_header: { 122 | data_writer.write_bytes(flv_data, video_tag->config_record_data->data, video_tag->config_record_data->size); 123 | break; 124 | } 125 | case aw_flv_v_h264_packet_type_nalu: { 126 | data_writer.write_bytes(flv_data, video_tag->frame_data->data, video_tag->frame_data->size); 127 | break; 128 | } 129 | case aw_flv_v_h264_packet_type_end_of_seq: { 130 | //nothing 131 | break; 132 | } 133 | } 134 | } 135 | 136 | static void aw_write_script_tag_body(aw_data **flv_data, aw_flv_script_tag *script_tag){ 137 | //纪录写入了多少字节 138 | data_writer.end_record_size(); 139 | data_writer.start_record_size(); 140 | 141 | //2表示类型,字符串 142 | data_writer.write_uint8(flv_data, 2); 143 | data_writer.write_string(flv_data, "onMetaData", 2); 144 | 145 | //数组类型:8 146 | data_writer.write_uint8(flv_data, 8); 147 | //数组长度:11 148 | data_writer.write_uint32(flv_data, 11); 149 | 150 | //28字节 151 | 152 | //写入duration 0表示double,1表示uint8 153 | data_writer.write_string(flv_data, "duration", 2); 154 | data_writer.write_uint8(flv_data, 0); 155 | data_writer.write_double(flv_data, script_tag->duration); 156 | //写入width 157 | data_writer.write_string(flv_data, "width", 2); 158 | data_writer.write_uint8(flv_data, 0); 159 | data_writer.write_double(flv_data, script_tag->width); 160 | //写入height 161 | data_writer.write_string(flv_data, "height", 2); 162 | data_writer.write_uint8(flv_data, 0); 163 | data_writer.write_double(flv_data, script_tag->height); 164 | //写入videodatarate 165 | data_writer.write_string(flv_data, "videodatarate", 2); 166 | data_writer.write_uint8(flv_data, 0); 167 | data_writer.write_double(flv_data, script_tag->video_data_rate); 168 | //写入framerate 169 | data_writer.write_string(flv_data, "framerate", 2); 170 | data_writer.write_uint8(flv_data, 0); 171 | data_writer.write_double(flv_data, script_tag->frame_rate); 172 | //写入videocodecid 173 | data_writer.write_string(flv_data, "videocodecid", 2); 174 | data_writer.write_uint8(flv_data, 0); 175 | data_writer.write_double(flv_data, script_tag->v_codec_id); 176 | //写入audiosamplerate 177 | data_writer.write_string(flv_data, "audiosamplerate", 2); 178 | data_writer.write_uint8(flv_data, 0); 179 | data_writer.write_double(flv_data, script_tag->a_sample_rate); 180 | //写入audiosamplesize 181 | data_writer.write_string(flv_data, "audiosamplesize", 2); 182 | data_writer.write_uint8(flv_data, 0); 183 | data_writer.write_double(flv_data, script_tag->a_sample_size); 184 | //写入stereo 185 | data_writer.write_string(flv_data, "stereo", 2); 186 | data_writer.write_uint8(flv_data, 1); 187 | data_writer.write_uint8(flv_data, script_tag->stereo); 188 | //写入a_codec_id 189 | data_writer.write_string(flv_data, "audiocodecid", 2); 190 | data_writer.write_uint8(flv_data, 0); 191 | data_writer.write_double(flv_data, script_tag->a_codec_id); 192 | //写入file_size 193 | data_writer.write_string(flv_data, "filesize", 2); 194 | data_writer.write_uint8(flv_data, 0); 195 | data_writer.write_double(flv_data, script_tag->file_size); 196 | 197 | //3字节的0x9表示metadata结束 198 | data_writer.write_uint24(flv_data, 9); 199 | 200 | //打印写入了多少字节 201 | AWLog("script tag body size = %ld", data_writer.record_size()); 202 | data_writer.end_record_size(); 203 | } 204 | 205 | static void aw_write_tag_body(aw_data **flv_data, aw_flv_common_tag *common_tag){ 206 | switch (common_tag->tag_type) { 207 | case aw_flv_tag_type_audio: { 208 | aw_write_audio_tag_body(flv_data, common_tag->audio_tag); 209 | break; 210 | } 211 | case aw_flv_tag_type_video: { 212 | aw_write_video_tag_body(flv_data, common_tag->video_tag); 213 | break; 214 | } 215 | case aw_flv_tag_type_script: { 216 | aw_write_script_tag_body(flv_data, common_tag->script_tag); 217 | break; 218 | } 219 | } 220 | } 221 | 222 | static void aw_write_tag_data_size(aw_data **flv_data, aw_flv_common_tag *common_tag){ 223 | data_writer.write_uint32(flv_data, common_tag->data_size); 224 | } 225 | 226 | extern void aw_write_flv_tag(aw_data **flv_data, aw_flv_common_tag *common_tag){ 227 | aw_write_tag_header(flv_data, common_tag); 228 | aw_write_tag_body(flv_data, common_tag); 229 | aw_write_tag_data_size(flv_data, common_tag); 230 | } 231 | 232 | extern void aw_write_flv_header(aw_data **flv_data){ 233 | uint8_t 234 | f = 'F', l = 'L', v = 'V',//FLV 235 | version = 1,//固定值 236 | av_flag = 5;//5表示av,5表示只有a,1表示只有v 237 | uint32_t flv_header_len = 9; 238 | data_writer.write_uint8(flv_data, f); 239 | data_writer.write_uint8(flv_data, l); 240 | data_writer.write_uint8(flv_data, v); 241 | data_writer.write_uint8(flv_data, version); 242 | data_writer.write_uint8(flv_data, av_flag); 243 | data_writer.write_uint32(flv_data, flv_header_len); 244 | 245 | //first previous tag size 246 | data_writer.write_uint32(flv_data, 0); 247 | } 248 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_encode_flv.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_encode_flv.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 19/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_encode_flv_h 10 | #define aw_encode_flv_h 11 | 12 | #include 13 | #include "aw_data.h" 14 | 15 | //flv tag type 16 | typedef enum aw_flv_tag_type { 17 | aw_flv_tag_type_audio = 8, 18 | aw_flv_tag_type_video = 9, 19 | aw_flv_tag_type_script = 18, 20 | } aw_flv_tag_type; 21 | 22 | //共6种(CodecID),这里只用h264 23 | typedef enum aw_flv_v_codec_id{ 24 | aw_flv_v_codec_id_H263 = 2, 25 | aw_flv_v_codec_id_H264 = 7, 26 | } aw_flv_v_codec_id; 27 | 28 | //共13种(即sound format),这里只用aac 29 | typedef enum aw_flv_a_codec_id{ 30 | aw_flv_a_codec_id_MP3 = 2, 31 | aw_flv_a_codec_id_AAC = 10, 32 | } aw_flv_a_codec_id; 33 | 34 | //sound size 8bit 16bit 35 | typedef enum aw_flv_a_sound_size{ 36 | aw_flv_a_sound_size_8_bit = 0, 37 | aw_flv_a_sound_size_16_bit = 1, 38 | } aw_flv_a_sound_size; 39 | 40 | //sound rate 5.5 11 22 44 kHz 41 | typedef enum aw_flv_a_sound_rate{ 42 | aw_flv_a_sound_rate_5_5kHZ = 0, 43 | aw_flv_a_sound_rate_11kHZ = 1, 44 | aw_flv_a_sound_rate_22kHZ = 2, 45 | aw_flv_a_sound_rate_44kHZ = 3, 46 | } aw_flv_a_sound_rate; 47 | 48 | //sound type mono/stereo 49 | typedef enum aw_flv_a_sound_type{ 50 | aw_flv_a_sound_type_mono = 0, 51 | aw_flv_a_sound_type_stereo = 1, 52 | } aw_flv_a_sound_type; 53 | 54 | //共5种 55 | typedef enum aw_flv_v_frame_type{ 56 | aw_flv_v_frame_type_key = 1,//关键帧 57 | aw_flv_v_frame_type_inner = 2,//非关键帧 58 | }aw_flv_v_frame_type; 59 | 60 | //h264 packet type 61 | typedef enum aw_flv_v_h264_packet_type{ 62 | aw_flv_v_h264_packet_type_seq_header = 0, 63 | aw_flv_v_h264_packet_type_nalu = 1, 64 | aw_flv_v_h264_packet_type_end_of_seq = 2, 65 | }aw_flv_v_h264_packet_type; 66 | 67 | typedef enum aw_flv_a_aac_packge_type{ 68 | aw_flv_a_aac_packge_type_aac_sequence_header = 0, 69 | aw_flv_a_aac_packge_type_aac_raw = 1, 70 | }aw_flv_a_aac_packge_type; 71 | 72 | struct aw_flv_audio_tag; 73 | struct aw_flv_video_tag; 74 | struct aw_flv_script_tag; 75 | 76 | // flv tags 77 | typedef struct aw_flv_common_tag{ 78 | aw_flv_tag_type tag_type;//tag类型,1字节 79 | //数据长度,3字节, 80 | // total tag size值为 sample_size + [script/audio/video]_tag_header_size + tag_header_size 81 | // tag header size = 11 82 | // [script/audio/video]_tag_header_size 会变化 83 | // tag body size = sample_size + [script/audio/video]_tag_header_size 84 | uint32_t data_size;//tag 总长度,包含11字节的 common_tag_header_size,包含自己的tag_header_size + tag_body_size; 85 | uint32_t header_size;//自己的tag header的长度 86 | uint32_t timestamp;//时间戳,3字节 87 | uint8_t timestamp_extend;//时间戳扩展位,1字节 88 | uint32_t stream_id;//总是0,3字节 89 | 90 | union{ 91 | struct aw_flv_audio_tag *audio_tag; 92 | struct aw_flv_video_tag *video_tag; 93 | struct aw_flv_script_tag *script_tag; 94 | }; 95 | } aw_flv_common_tag; 96 | 97 | typedef struct aw_flv_script_tag{ 98 | aw_flv_common_tag common_tag; 99 | double duration; 100 | double width; 101 | double height; 102 | double video_data_rate; 103 | double frame_rate; 104 | double v_frame_rate; 105 | double a_frame_rate; 106 | double v_codec_id; 107 | double a_sample_rate; 108 | double a_sample_size; 109 | uint8_t stereo; 110 | double a_codec_id; 111 | double file_size; 112 | } aw_flv_script_tag; 113 | 114 | extern aw_flv_script_tag *alloc_aw_flv_script_tag(); 115 | extern void free_aw_flv_script_tag(aw_flv_script_tag **); 116 | 117 | typedef struct aw_flv_audio_tag{ 118 | aw_flv_common_tag common_tag; 119 | aw_flv_a_codec_id sound_format;//声音格式,4bit(0.5字节) 120 | aw_flv_a_sound_rate sound_rate;//声音频率,2bit 121 | aw_flv_a_sound_size sound_size;//声音尺寸, 1bit 122 | aw_flv_a_sound_type sound_type;//声音类型, 1bit 123 | aw_flv_a_aac_packge_type aac_packet_type;//aac 包类型,1字节,如果sound format==10(AAC)会有这个字段,否则没有。 124 | 125 | //aac sqeuence header,包含了aac转流所需的必备内容,需要作为第一个audio tag发送。 126 | aw_data *config_record_data;//audio config data 127 | 128 | aw_data *frame_data;//audio frame data 129 | } aw_flv_audio_tag; 130 | 131 | extern aw_flv_audio_tag *alloc_aw_flv_audio_tag(); 132 | extern void free_aw_flv_audio_tag(aw_flv_audio_tag **); 133 | 134 | typedef struct aw_flv_video_tag{ 135 | aw_flv_common_tag common_tag; 136 | aw_flv_v_frame_type frame_type;//帧类型,是否关键帧,4bit(0.5字节) 137 | aw_flv_v_codec_id codec_id;//编码器id,4bit 138 | 139 | //h264才有 140 | aw_flv_v_h264_packet_type h264_package_type;//h264包类型,1字节 141 | uint32_t h264_composition_time;//h264 时间调整(即cts,pts = dts + cts), 3字节 142 | 143 | //avc sequence header, 包含h264需要的重要信息(sps,pps),需要在第一个videotag时发送 144 | aw_data *config_record_data;//video config data 145 | 146 | aw_data *frame_data;//video frame data 147 | } aw_flv_video_tag; 148 | 149 | extern aw_flv_video_tag *alloc_aw_flv_video_tag(); 150 | extern void free_aw_flv_video_tag(aw_flv_video_tag **); 151 | 152 | //构造flv的方法 153 | //写入header 154 | extern void aw_write_flv_header(aw_data **flv_data); 155 | //写入flv tag 156 | extern void aw_write_flv_tag(aw_data **flv_data, aw_flv_common_tag *common_tag); 157 | 158 | #endif /* aw_encode_flv_h */ 159 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_file.c: -------------------------------------------------------------------------------- 1 | // 2 | // aw_file.c 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 14/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #include "aw_file.h" 10 | #include "aw_utils.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | extern int8_t aw_is_file_exist(const char *file_path){ 18 | FILE *file = fopen(file_path, "r"); 19 | if(file == NULL){ 20 | if (errno == ENOENT) { 21 | return 0; 22 | }else{ 23 | AWLog("file open error errno=%d", errno); 24 | return -1; 25 | } 26 | } 27 | fclose(file); 28 | return 1; 29 | } 30 | 31 | extern size_t aw_file_size(const char *file_path){ 32 | FILE * file = fopen(file_path, "r"); 33 | if (file) { 34 | fseek(file, 0, SEEK_END); 35 | size_t file_size = ftell(file); 36 | fclose(file); 37 | return file_size; 38 | } 39 | return 0; 40 | } 41 | 42 | extern int8_t aw_remove_file(const char *file_path){ 43 | if (aw_is_file_exist(file_path)) { 44 | return (int8_t)remove(file_path) == 0; 45 | } 46 | return 1; 47 | } 48 | 49 | extern int8_t aw_remove_dir(const char *file_dir){ 50 | DIR *dp = NULL; 51 | struct dirent *entry = NULL; 52 | struct stat stat; 53 | if ((dp = opendir(file_dir)) == NULL) { 54 | return 0; 55 | } 56 | chdir (file_dir); 57 | while ((entry = readdir(dp)) != NULL) 58 | { 59 | lstat(entry->d_name, &stat); 60 | if (S_ISREG(stat.st_mode)) 61 | { 62 | remove(entry->d_name); 63 | } 64 | } 65 | 66 | return 1; 67 | } 68 | 69 | extern aw_data *aw_read_data_from_file(const char *file_path){ 70 | FILE * file = fopen(file_path, "r+"); 71 | if (file) { 72 | size_t file_size = aw_file_size(file_path); 73 | aw_data *mp4_data = alloc_aw_data((int)file_size); 74 | size_t read_item_count = fread(mp4_data->data, file_size, 1, file); 75 | fclose(file); 76 | if (!read_item_count) { 77 | free_aw_data(&mp4_data); 78 | return NULL; 79 | } 80 | return mp4_data; 81 | } 82 | return NULL; 83 | } 84 | 85 | extern int8_t aw_write_data_to_file(const char *file_path, aw_data *data){ 86 | FILE *file = fopen(file_path, "w"); 87 | if (file) { 88 | size_t file_size = data->size; 89 | size_t write_item_count = fwrite(data->data, file_size, 1, file); 90 | fflush(file); 91 | fclose(file); 92 | return write_item_count != 0; 93 | } 94 | return 0; 95 | } 96 | 97 | extern void aw_test_file(const char *dir){ 98 | char *short_name = "/tmp.t"; 99 | size_t file_name_size = strlen(dir) + strlen(short_name) + 1; 100 | char *file_name = aw_alloc(file_name_size); 101 | memset(file_name, 0, file_name_size); 102 | memcpy(file_name, dir, strlen(dir)); 103 | memcpy(file_name + strlen(dir), short_name, strlen(short_name)); 104 | 105 | //文件是否存在 106 | AWLog("1 file %s is exist(%d)", file_name, aw_is_file_exist(file_name)); 107 | 108 | //文件写入 109 | aw_data *awdata = alloc_aw_data(0); 110 | char *datastr = "nihao hahaha 你好"; 111 | memcpy_aw_data(&awdata, datastr, (int32_t)strlen(datastr) + 1); 112 | 113 | if(aw_write_data_to_file(file_name, awdata)){ 114 | AWLog("文件写入成功"); 115 | }else{ 116 | AWLog("文件写入失败"); 117 | } 118 | free_aw_data(&awdata); 119 | //文件是否存在 120 | AWLog("2 file %s is exist(%d)", file_name, aw_is_file_exist(file_name)); 121 | AWLog("3 file %s size(%ld)", file_name, aw_file_size(file_name)); 122 | 123 | //文件读取 124 | aw_data *readdata = aw_read_data_from_file(file_name); 125 | if (readdata) { 126 | AWLog("文件读取成功 %s", readdata->data); 127 | free_aw_data(&readdata); 128 | }else{ 129 | AWLog("文件读取失败"); 130 | } 131 | 132 | //文件移除 133 | if(aw_remove_file(file_name)){ 134 | AWLog("文件移除成功"); 135 | }else{ 136 | AWLog("文件移除失败"); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_file.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_file.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 14/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_file_h 10 | #define aw_file_h 11 | 12 | #include 13 | #include "aw_data.h" 14 | 15 | //iOS无法读取 main bundle 中的文件 16 | 17 | //文件是否存在 18 | extern int8_t aw_is_file_exist(const char *file_path); 19 | 20 | //文件尺寸 21 | extern size_t aw_file_size(const char *file_path); 22 | 23 | //文件移除 24 | extern int8_t aw_remove_file(const char *file_path); 25 | 26 | //文件夹移除 27 | extern int8_t aw_remove_dir(const char *file_dir); 28 | 29 | //读取数据 30 | extern aw_data *aw_read_data_from_file(const char *file_path); 31 | 32 | //写入数据 33 | extern int8_t aw_write_data_to_file(const char *file_path, aw_data *data); 34 | 35 | //测试本文件 36 | extern void aw_test_file(const char *dir); 37 | 38 | #endif /* aw_file_h */ 39 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_mp4box.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_mp4box.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 12/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_mp4box_h 10 | #define aw_mp4box_h 11 | 12 | #include 13 | #include "aw_dict.h" 14 | #include "aw_data.h" 15 | 16 | //mp4 文件 17 | typedef struct aw_mp4_box{ 18 | aw_dict *parsed_data; 19 | int32_t type; 20 | 21 | //inner 22 | int64_t size; 23 | char *type_string; 24 | char uuid[16]; 25 | aw_data *data; 26 | aw_dict *children; 27 | struct aw_mp4_box *parent_box; 28 | struct aw_mp4_box *root_box; 29 | } aw_mp4_box; 30 | 31 | extern uint32_t aw_mp4_bytes_fm_ascii(uint8_t a, uint8_t b, uint8_t c, uint8_t d); 32 | 33 | extern void aw_mp4_int32_to_ascii_str(uint32_t, char [5]); 34 | 35 | extern aw_mp4_box *alloc_aw_mp4_box(); 36 | extern void free_aw_mp4_box(aw_mp4_box **); 37 | 38 | extern void aw_mp4box_parse_data(aw_mp4_box *box, aw_data *mp4_data); 39 | 40 | extern aw_mp4_box *aw_mp4box_find_box(aw_mp4_box *box, const char *type_name); 41 | 42 | extern void aw_test_mp4_box(void *data, int32_t len); 43 | 44 | #endif /* aw_mp4box_h */ 45 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_parse_mp4.c: -------------------------------------------------------------------------------- 1 | // 2 | // aw_parse_mp4.c 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 15/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #include "aw_parse_mp4.h" 10 | #include "aw_data.h" 11 | #include "aw_dict.h" 12 | #include "aw_mp4box.h" 13 | #include "aw_utils.h" 14 | 15 | static aw_parsed_mp4 *aw_parse_mp4_boxes(aw_dict *mp4_box_dict, aw_data *mp4_file_data); 16 | 17 | extern aw_mp4_av_sample *alloc_aw_mp4_av_sample(){ 18 | aw_mp4_av_sample *av_sample = aw_alloc(sizeof(aw_mp4_av_sample)); 19 | memset(av_sample, 0, sizeof(aw_mp4_av_sample)); 20 | return av_sample; 21 | } 22 | 23 | extern void free_aw_mp4_av_sample(aw_mp4_av_sample **av_sample){ 24 | aw_free(*av_sample); 25 | *av_sample = NULL; 26 | } 27 | 28 | extern aw_parsed_mp4 *alloc_aw_parsed_mp4(){ 29 | aw_parsed_mp4 *parsed_mp4 = aw_alloc(sizeof(aw_parsed_mp4)); 30 | memset(parsed_mp4, 0, sizeof(aw_parsed_mp4)); 31 | parsed_mp4->frames = alloc_aw_array(0); 32 | return parsed_mp4; 33 | } 34 | 35 | extern void free_aw_parsed_mp4(aw_parsed_mp4 **parsed_mp4){ 36 | aw_parsed_mp4 *inner_parsed_mp4 = *parsed_mp4; 37 | if (inner_parsed_mp4->video_config_record) { 38 | free_aw_data(&inner_parsed_mp4->video_config_record); 39 | } 40 | if (inner_parsed_mp4->audio_config_record) { 41 | free_aw_data(&inner_parsed_mp4->audio_config_record); 42 | } 43 | if (inner_parsed_mp4->frames) { 44 | free_aw_array(&inner_parsed_mp4->frames); 45 | } 46 | if (inner_parsed_mp4->mp4_file_data) { 47 | free_aw_data(&inner_parsed_mp4->mp4_file_data); 48 | } 49 | aw_free(*parsed_mp4); 50 | *parsed_mp4 = NULL; 51 | } 52 | 53 | static void aw_free_mp4_box_for_dict(aw_mp4_box *box, int extra){ 54 | free_aw_mp4_box(&box); 55 | } 56 | 57 | static void aw_free_mp4_av_sample_for_parsed_mp4(aw_mp4_av_sample *av_sample, int extra){ 58 | free_aw_mp4_av_sample(&av_sample); 59 | } 60 | 61 | extern aw_parsed_mp4 *aw_parse_mp4_file_data(const uint8_t *mp4_file_data, size_t len){ 62 | aw_data *mp4_data = alloc_aw_data((int32_t)len); 63 | memcpy_aw_data(&mp4_data, mp4_file_data, (int32_t)len); 64 | 65 | aw_dict *mp4box_dict = alloc_aw_dict(); 66 | 67 | data_reader.start_read(mp4_data); 68 | while (data_reader.remain_count(mp4_data)) { 69 | aw_mp4_box *mp4_box = alloc_aw_mp4_box(); 70 | aw_mp4box_parse_data(mp4_box, mp4_data); 71 | aw_dict_set_release_pointer(mp4box_dict, mp4_box->type_string, mp4_box, (void (*)(void *, int))aw_free_mp4_box_for_dict, 0, 1); 72 | } 73 | 74 | aw_parsed_mp4 *parsed_mp4 = aw_parse_mp4_boxes(mp4box_dict, mp4_data); 75 | 76 | free_aw_dict(&mp4box_dict); 77 | 78 | return parsed_mp4; 79 | } 80 | 81 | static void aw_parse_stbl_box(aw_mp4_box *stbl_box, aw_parsed_mp4 *parsed_mp4,int8_t is_video){ 82 | //stts和ctts决定了每个sample的解析和播放时间戳 83 | //stts是具有相同时间间隔的sample数量。 84 | //duration = sample_count1*duration1 + ... + sample_countN * durationN 85 | //通过stts和ctts能够计算每个sample的dts和pts 86 | aw_array *sample_duration_array = alloc_aw_array(0); 87 | aw_mp4_box *stts_box = aw_mp4box_find_box(stbl_box, "stts"); 88 | int sample_duration_accum = 0; 89 | aw_dict **stts_box_entries = aw_dict_get_release_pointer(stts_box->parsed_data, "entries"); 90 | int i = 0; 91 | int sample_duration_count = aw_dict_get_int(stts_box->parsed_data, "time_to_sample_count"); 92 | for (; i < sample_duration_count; i++) { 93 | aw_dict *time_to_sample = stts_box_entries[i]; 94 | int sample_count = aw_dict_get_int(time_to_sample, "sample_count"); 95 | int sample_duration = aw_dict_get_int(time_to_sample, "sample_duration"); 96 | int j = 0; 97 | for (; j < sample_count; j++) { 98 | aw_array_add_int(&sample_duration_array, sample_duration_accum); 99 | sample_duration_accum += sample_duration; 100 | } 101 | } 102 | aw_array_add_int(&sample_duration_array, sample_duration_accum); 103 | 104 | //获取ctts,修正stts时间 105 | aw_array *video_sample_duration_offset_array = NULL; 106 | if (is_video) { 107 | aw_mp4_box * ctts_box = aw_mp4box_find_box(stbl_box, "ctts"); 108 | if (ctts_box) { 109 | video_sample_duration_offset_array = 110 | alloc_aw_array(0); 111 | int ctts_entries_count = aw_dict_get_int(ctts_box->parsed_data, "sample_count"); 112 | aw_dict **ctts_box_entries = aw_dict_get_release_pointer(ctts_box->parsed_data, "entries"); 113 | i = 0; 114 | for (; i < ctts_entries_count; i++) { 115 | aw_dict *entry = ctts_box_entries[i]; 116 | int sample_index = aw_dict_get_int(entry, "sample_count"); 117 | int sample_offset = aw_dict_get_int(entry, "sample_offset"); 118 | int j = 0; 119 | for (; j < sample_index; j++) { 120 | aw_array_add_int(&video_sample_duration_offset_array, sample_offset); 121 | } 122 | } 123 | } 124 | } 125 | 126 | //stsc描述了chunk信息,包含几个chunk,每个chunk中有几个samples,值得注意的是,chunk以1开头。chunk其实是连续的多个samples,相当于语言中的数组。没有别的复杂含义。而chunk的firstchunk可能会跳跃,所以,需要特别处理。 127 | aw_mp4_box *stsc_box = aw_mp4box_find_box(stbl_box, "stsc"); 128 | //stco纪录了每个chunk在文件中的位置。 129 | aw_mp4_box *stco_box = aw_mp4box_find_box(stbl_box, "stco"); 130 | //stsz纪录了每个sample的size。通过结合上述3个box,能够精确获得每个sample的位置和大小。 131 | aw_mp4_box *stsz_box = aw_mp4box_find_box(stbl_box, "stsz"); 132 | //stss纪录了关键帧,它纪录的是sample的下标 133 | aw_mp4_box *stss_box = aw_mp4box_find_box(stbl_box, "stss"); 134 | 135 | int32_t all_chunks_dict_count = aw_dict_get_int(stsc_box->parsed_data, "entry_count"); 136 | aw_dict **all_chunks_dict = aw_dict_get_release_pointer(stsc_box->parsed_data, "entries"); 137 | 138 | // int32_t all_chunks_pos_dict_count = aw_dict_get_int(stco_box->parsed_data, "chunk_offset_count"); 139 | aw_dict **all_chunks_pos_dict = aw_dict_get_release_pointer(stco_box->parsed_data, "entries"); 140 | 141 | // int32_t all_sample_size_dict_count = aw_dict_get_int(stsz_box->parsed_data, "sample_size_count"); 142 | aw_dict **all_sample_size_dict = aw_dict_get_release_pointer(stsz_box->parsed_data, "entries"); 143 | 144 | int32_t all_key_frame_indexes_dict_count = 0; 145 | aw_dict **all_key_frame_indexes_dict = NULL; 146 | if (stss_box) { 147 | all_key_frame_indexes_dict_count = aw_dict_get_int(stss_box->parsed_data, "sync_sample_count"); 148 | all_key_frame_indexes_dict = aw_dict_get_release_pointer(stss_box->parsed_data, "entries"); 149 | } 150 | 151 | int32_t sample_index = 0; 152 | double last_video_dts = -1; 153 | double last_audio_dts = -1; 154 | i = 0; 155 | for (; i < all_chunks_dict_count; i++) { 156 | aw_dict *one_chunk_dict = all_chunks_dict[i]; 157 | int32_t first_chunk = aw_dict_get_int(one_chunk_dict, "first_chunk"); 158 | int32_t next_chunk = -1; 159 | if (i != all_chunks_dict_count - 1) { 160 | aw_dict *next_chunk_dict = all_chunks_dict[i + 1]; 161 | next_chunk = aw_dict_get_int(next_chunk_dict, "first_chunk"); 162 | }else{ 163 | next_chunk = all_chunks_dict_count - 1; 164 | } 165 | 166 | int chunk_index = first_chunk - 1; 167 | for (; chunk_index < next_chunk - 1 ; chunk_index++) { 168 | int32_t samples_in_chunk = aw_dict_get_int(one_chunk_dict, "samples_one_chunk"); 169 | int32_t chunk_pos = aw_dict_get_int(all_chunks_pos_dict[chunk_index], "offset"); 170 | int32_t next_sample_pos = 0; 171 | int j = 0; 172 | for (; j < samples_in_chunk; j++, sample_index++) { 173 | int32_t curr_sample_size = aw_dict_get_int(all_sample_size_dict[sample_index], "one_sample_size"); 174 | int32_t curr_sample_pos = chunk_pos + next_sample_pos; 175 | 176 | next_sample_pos += curr_sample_size; 177 | 178 | uint8_t is_key_frame = 0; 179 | int k = 0; 180 | for (; k < all_key_frame_indexes_dict_count; k++) { 181 | aw_dict *one_index_dict = all_key_frame_indexes_dict[k]; 182 | int key_frame_index = aw_dict_get_int(one_index_dict, "sample_index"); 183 | if (key_frame_index == sample_index + 1) { 184 | is_key_frame = 1; 185 | break; 186 | } 187 | } 188 | 189 | double dts = (double)aw_array_element_at_index(sample_duration_array, sample_index)->int_value; 190 | if (is_video) { 191 | dts /= parsed_mp4->video_time_scale; 192 | if (!parsed_mp4->video_frame_rate) { 193 | if (last_video_dts == -1) { 194 | last_video_dts = dts; 195 | }else{ 196 | parsed_mp4->video_frame_rate = dts - last_video_dts; 197 | } 198 | } 199 | }else{ 200 | dts /= parsed_mp4->audio_sample_rate; 201 | if (parsed_mp4->audio_frame_rate == 0) { 202 | if (last_audio_dts == -1) { 203 | last_audio_dts = dts; 204 | }else{ 205 | parsed_mp4->audio_frame_rate = dts - last_audio_dts; 206 | } 207 | } 208 | } 209 | 210 | double composite_time = -1; 211 | if (is_video && video_sample_duration_offset_array &&video_sample_duration_offset_array->count > 0) { 212 | composite_time = (double)aw_array_element_at_index(video_sample_duration_offset_array, sample_index)->int_value; 213 | composite_time /= parsed_mp4->video_time_scale; 214 | } 215 | 216 | aw_mp4_av_sample *mp4_av_sample = alloc_aw_mp4_av_sample(); 217 | mp4_av_sample->data_start_in_file = curr_sample_pos; 218 | mp4_av_sample->sample_size = curr_sample_size; 219 | mp4_av_sample->is_key_frame = is_key_frame; 220 | mp4_av_sample->dts = dts + (is_video ? parsed_mp4->video_start_dts : parsed_mp4->audio_start_dts); 221 | if (composite_time >= 0) { 222 | mp4_av_sample->is_valid_composite_time = 1; 223 | mp4_av_sample->composite_time = composite_time; 224 | }else{ 225 | mp4_av_sample->is_valid_composite_time = 0; 226 | mp4_av_sample->composite_time = 0; 227 | } 228 | mp4_av_sample->is_video = is_video; 229 | aw_array_add_release_pointer(&parsed_mp4->frames, mp4_av_sample, (void (*)(void *, int))aw_free_mp4_av_sample_for_parsed_mp4, 0); 230 | } 231 | } 232 | } 233 | 234 | if(video_sample_duration_offset_array){ 235 | free_aw_array(&video_sample_duration_offset_array); 236 | } 237 | 238 | if (sample_duration_array) { 239 | free_aw_array(&sample_duration_array); 240 | } 241 | } 242 | 243 | static void aw_parse_trak_box(aw_mp4_box *trak_box, aw_parsed_mp4 *parsed_mp4){ 244 | aw_mp4_box *mdia_box = aw_mp4box_find_box(trak_box, "mdia"); 245 | aw_mp4_box *hdlr_box = aw_mp4box_find_box(mdia_box, "hdlr"); 246 | aw_mp4_box *stbl_box = aw_mp4box_find_box(mdia_box, "minf.stbl"); 247 | aw_mp4_box *mdhd_box = aw_mp4box_find_box(mdia_box, "mdhd"); 248 | aw_mp4_box *stsd_box = aw_mp4box_find_box(stbl_box, "stsd"); 249 | aw_mp4_box *elst_box = aw_mp4box_find_box(trak_box, "edts.elst"); 250 | 251 | int32_t start_offset = 0; 252 | int32_t empty_duration = 0; 253 | if (elst_box) { 254 | int edit_start_index = 0; 255 | int32_t start_time = 0; 256 | int edit_list_count = aw_dict_get_int(elst_box->parsed_data, "edit_list_count"); 257 | aw_dict **entries = aw_dict_get_pointer(elst_box->parsed_data, "entries"); 258 | int i = 0; 259 | for (; i < edit_list_count; i++) { 260 | aw_dict *entry = entries[i]; 261 | int32_t duration = aw_dict_get_int(entry, "duration"); 262 | int32_t time = aw_dict_get_int(entry, "start_time"); 263 | if (i == 0 && time == -1) { 264 | empty_duration = duration; 265 | edit_start_index = 1; 266 | }else if(i == edit_start_index && time >= 0){ 267 | start_time = time; 268 | } 269 | } 270 | start_offset = start_time; 271 | } 272 | 273 | const char *handle_type = aw_dict_get_str(hdlr_box->parsed_data, "handle_type"); 274 | if (!strcmp(handle_type, "vide")) { 275 | aw_mp4_box *tkhd_box = aw_mp4box_find_box(trak_box, "tkhd"); 276 | parsed_mp4->video_width = aw_dict_get_double(tkhd_box->parsed_data, "width"); 277 | parsed_mp4->video_height = aw_dict_get_double(tkhd_box->parsed_data, "height"); 278 | parsed_mp4->video_time_scale = aw_dict_get_int(mdhd_box->parsed_data, "time_scale"); 279 | 280 | aw_mp4_box *avcc_box = aw_mp4box_find_box(stsd_box, "avc1.avcC"); 281 | parsed_mp4->video_config_record = copy_aw_data(aw_dict_get_pointer(avcc_box->parsed_data, "avcc_data")); 282 | 283 | parsed_mp4->video_start_dts = -(start_offset + empty_duration / 90000 * parsed_mp4->video_time_scale) / parsed_mp4->video_time_scale; 284 | 285 | aw_parse_stbl_box(stbl_box, parsed_mp4, 1); 286 | }else if(!strcmp(handle_type, "soun")){ 287 | aw_mp4_box *mp4a_box = aw_mp4box_find_box(stsd_box, "mp4a"); 288 | parsed_mp4->audio_sample_size = aw_dict_get_int(mp4a_box->parsed_data, "sample_size"); 289 | parsed_mp4->audio_is_stereo = aw_dict_get_int(mp4a_box->parsed_data, "channel_count") > 1; 290 | parsed_mp4->audio_sample_rate = aw_dict_get_int(mdhd_box->parsed_data, "time_scale"); 291 | aw_mp4_box *esds_box = aw_mp4box_find_box(mp4a_box, "esds"); 292 | if (!esds_box) { 293 | esds_box = aw_mp4box_find_box(mp4a_box, "wave.esds"); 294 | } 295 | 296 | if (esds_box) { 297 | parsed_mp4->audio_config_record = copy_aw_data(aw_dict_get_pointer(esds_box->parsed_data, "decoder_config")); 298 | } 299 | 300 | parsed_mp4->audio_start_dts = -(start_offset + empty_duration / 90000 * parsed_mp4->audio_sample_rate) / parsed_mp4->audio_sample_rate; 301 | 302 | aw_parse_stbl_box(stbl_box, parsed_mp4, 0); 303 | }else{ 304 | return; 305 | } 306 | } 307 | 308 | static aw_array_sort_compare_result aw_parse_mp4_sort_frames_compare_func(aw_array_element *ele1, aw_array_element *ele2){ 309 | aw_mp4_av_sample *av_sample1 = ele1->pointer_value; 310 | aw_mp4_av_sample *av_sample2 = ele2->pointer_value; 311 | if (av_sample1->dts > av_sample2->dts) { 312 | return aw_array_sort_compare_result_great; 313 | }else if(av_sample1->dts < av_sample2->dts){ 314 | return aw_array_sort_compare_result_less; 315 | }else{ 316 | if (!av_sample1->is_video && av_sample2->is_video) { 317 | return aw_array_sort_compare_result_great; 318 | }else{ 319 | if (av_sample1->data_start_in_file > av_sample2->data_start_in_file) { 320 | return aw_array_sort_compare_result_great; 321 | }else if(av_sample1->data_start_in_file < av_sample2->data_start_in_file){ 322 | return aw_array_sort_compare_result_less; 323 | }else{ 324 | return aw_array_sort_compare_result_equal; 325 | } 326 | } 327 | } 328 | } 329 | 330 | static void aw_parse_mp4_sort_frames(aw_parsed_mp4 *parsed_mp4, aw_array_sort_func sort_func){ 331 | sort_func(parsed_mp4->frames, aw_array_sort_policy_ascending, aw_parse_mp4_sort_frames_compare_func); 332 | } 333 | 334 | static aw_parsed_mp4 *aw_parse_mp4_boxes(aw_dict *mp4box_dict, aw_data *mp4_file_data){ 335 | aw_parsed_mp4 *parsed_mp4 = alloc_aw_parsed_mp4(); 336 | parsed_mp4->mp4_file_data = mp4_file_data; 337 | 338 | aw_mp4_box *moov_box = aw_dict_get_pointer(mp4box_dict, "moov"); 339 | 340 | //视频长度 341 | aw_mp4_box *mvhd_box = aw_mp4box_find_box(moov_box, "mvhd"); 342 | parsed_mp4->duration = (double)aw_dict_get_int(mvhd_box->parsed_data, "duration") / aw_dict_get_int(mvhd_box->parsed_data, "time_scale"); 343 | 344 | parsed_mp4->video_data_rate = mp4_file_data->size / parsed_mp4->duration * 8; 345 | 346 | int i = 0; 347 | while (1) { 348 | char trak_name[32]; 349 | memset(trak_name, 0, 32); 350 | sprintf(trak_name, "trak@%d", i); 351 | aw_mp4_box *trak_box = aw_mp4box_find_box(moov_box, trak_name); 352 | if (trak_box) { 353 | aw_parse_trak_box(trak_box, parsed_mp4); 354 | i++; 355 | }else{ 356 | break; 357 | } 358 | } 359 | 360 | //给parsed_mp4->frames 排序 361 | 362 | aw_parse_mp4_sort_frames(parsed_mp4, aw_array_sort_quick); 363 | 364 | return parsed_mp4; 365 | } 366 | 367 | //测试 368 | extern void aw_parse_mp4_test(const uint8_t *mp4_file_data, size_t len){ 369 | aw_uninit_debug_alloc(); 370 | aw_init_debug_alloc(); 371 | 372 | aw_parsed_mp4 *parsed_mp4 = aw_parse_mp4_file_data(mp4_file_data, len); 373 | 374 | free_aw_parsed_mp4(&parsed_mp4); 375 | 376 | aw_print_alloc_description(); 377 | } 378 | 379 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_parse_mp4.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_parse_mp4.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 15/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_parse_mp4_h 10 | #define aw_parse_mp4_h 11 | 12 | #include 13 | #include "aw_dict.h" 14 | #include "aw_alloc.h" 15 | #include "aw_data.h" 16 | #include "aw_array.h" 17 | 18 | //mp4视频/音频帧 19 | typedef struct aw_mp4_av_sample{ 20 | //帧位于文件的哪个位置 21 | uint32_t data_start_in_file; 22 | //数据长度 23 | uint32_t sample_size; 24 | //是否关键帧 25 | uint8_t is_key_frame; 26 | //时间戳 27 | double dts; 28 | //即h264的cts pts = dts + cts 29 | double composite_time; 30 | //是否有composite_time 31 | int8_t is_valid_composite_time; 32 | //视频帧还是音频 33 | uint8_t is_video; 34 | } aw_mp4_av_sample; 35 | 36 | extern aw_mp4_av_sample *alloc_aw_mp4_av_sample(); 37 | 38 | extern void free_aw_mp4_av_sample(aw_mp4_av_sample **av_sample); 39 | 40 | typedef struct aw_parsed_mp4 { 41 | //时长 42 | double duration; 43 | 44 | //-------视频参数-------- 45 | //视频码率 46 | double video_data_rate; 47 | //视频帧率:一帧多长时间 48 | double video_frame_rate; 49 | //视频宽高 50 | double video_width; 51 | double video_height; 52 | //视频时间标尺,采样数 53 | double video_time_scale; 54 | 55 | //视频config record for h264 56 | aw_data *video_config_record; 57 | 58 | double video_start_dts; 59 | 60 | //-------音频参数-------- 61 | //音频帧率:一帧多长时间 62 | double audio_frame_rate; 63 | //音频样本大小 64 | double audio_sample_size; 65 | //是否立体声 66 | int8_t audio_is_stereo; 67 | //音频采样率,同video的time_scale duration /time_scale = 时间(s) 68 | int32_t audio_sample_rate; 69 | //音频config record for h264 70 | aw_data *audio_config_record; 71 | 72 | aw_data *mp4_file_data; 73 | 74 | double audio_start_dts; 75 | 76 | //音视频帧数据 77 | aw_array *frames; 78 | } aw_parsed_mp4; 79 | 80 | extern aw_parsed_mp4 *alloc_aw_parsed_mp4(); 81 | extern void free_aw_parsed_mp4(aw_parsed_mp4 **parsed_mp4); 82 | 83 | //解析函数 84 | extern aw_parsed_mp4 *aw_parse_mp4_file_data(const uint8_t *mp4_file_data, size_t len); 85 | 86 | //测试 87 | extern void aw_parse_mp4_test(const uint8_t *mp4_file_data, size_t len); 88 | 89 | #endif /* aw_parse_mp4_h */ 90 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_pushstream.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_pushstream.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 20/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_pushstream_h 10 | #define aw_pushstream_h 11 | 12 | #include "aw_rtmp.h" 13 | #include "aw_alloc.h" 14 | #include "aw_utils.h" 15 | #include "aw_data.h" 16 | #include "aw_array.h" 17 | #include "aw_dict.h" 18 | #include "aw_file.h" 19 | #include "aw_mp4box.h" 20 | #include "aw_parse_mp4.h" 21 | #include "aw_encode_flv.h" 22 | #include "aw_convert_mp4_to_flv.h" 23 | 24 | #endif /* aw_pushstream_h */ 25 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_rtmp.c: -------------------------------------------------------------------------------- 1 | // 2 | // aw_rtmp.c 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 20/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #include "aw_rtmp.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "aw_utils.h" 15 | 16 | extern void aw_init_rtmp_context(aw_rtmp_context *ctx, const char *rtmp_url, aw_rtmp_state_changed_cb state_changed_cb){ 17 | ctx->rtmp = NULL; 18 | ctx->rtmp_state = aw_rtmp_state_idle; 19 | 20 | memset(ctx->rtmp_url, 0, 256); 21 | size_t url_size = strlen(rtmp_url); 22 | if (url_size <= 0 || url_size >= 256) { 23 | AWLog("[error] when init rtmp_context rtmp_url is error"); 24 | }else{ 25 | memcpy(ctx->rtmp_url, rtmp_url, url_size); 26 | } 27 | 28 | ctx->write_error_retry_curr_time = 0; 29 | ctx->write_error_retry_time_limit = 5; 30 | ctx->open_error_retry_curr_time = 0; 31 | ctx->open_error_retry_time_limit = 3; 32 | ctx->state_changed_cb = state_changed_cb; 33 | } 34 | 35 | extern aw_rtmp_context *alloc_aw_rtmp_context(const char *rtmp_url, aw_rtmp_state_changed_cb state_changed_cb){ 36 | aw_rtmp_context *ctx = aw_alloc(sizeof(aw_rtmp_context)); 37 | memset(ctx, 0, sizeof(aw_rtmp_context)); 38 | aw_init_rtmp_context(ctx, rtmp_url, state_changed_cb); 39 | return ctx; 40 | } 41 | 42 | extern void free_aw_rtmp_context(aw_rtmp_context **ctx){ 43 | aw_rtmp_context *inner_ctx = *ctx; 44 | 45 | if (inner_ctx) { 46 | if (inner_ctx->rtmp) { 47 | aw_rtmp_close(inner_ctx); 48 | } 49 | aw_free(inner_ctx); 50 | } 51 | *ctx = NULL; 52 | } 53 | 54 | static void aw_set_rtmp_state(aw_rtmp_context *ctx, aw_rtmp_state new_state); 55 | 56 | static void aw_rtmp_state_changed_inner(aw_rtmp_context *ctx, aw_rtmp_state old_state, aw_rtmp_state new_state){ 57 | switch (new_state) { 58 | case aw_rtmp_state_idle: { 59 | ctx->write_error_retry_curr_time = 0; 60 | ctx->is_header_sent = 0; 61 | 62 | //总时间 63 | ctx->total_duration += ctx->current_time_stamp; 64 | 65 | //当前流时间戳 66 | ctx->current_time_stamp = 0; 67 | break; 68 | } 69 | case aw_rtmp_state_opened: { 70 | ctx->open_error_retry_curr_time = 0; 71 | break; 72 | } 73 | case aw_rtmp_state_closed: { 74 | aw_set_rtmp_state(ctx, aw_rtmp_state_idle); 75 | break; 76 | } 77 | case aw_rtmp_state_error_write: { 78 | aw_set_rtmp_state(ctx, old_state); 79 | //写入错误次数过多 重连 80 | if (ctx->write_error_retry_curr_time >= ctx->write_error_retry_time_limit) { 81 | aw_rtmp_close(ctx); 82 | aw_rtmp_open(ctx); 83 | }else{ 84 | ctx->write_error_retry_curr_time++; 85 | } 86 | break; 87 | } 88 | case aw_rtmp_state_error_open: { 89 | aw_set_rtmp_state(ctx, aw_rtmp_state_idle); 90 | //open错误次数过多,认为网络错误 91 | if (ctx->open_error_retry_curr_time >= ctx->open_error_retry_curr_time) { 92 | aw_set_rtmp_state(ctx, aw_rtmp_state_error_net); 93 | }else{ 94 | ctx->open_error_retry_curr_time++; 95 | } 96 | break; 97 | } 98 | case aw_rtmp_state_error_net:{ 99 | ctx->open_error_retry_curr_time = 0; 100 | aw_set_rtmp_state(ctx, aw_rtmp_state_idle); 101 | break; 102 | } 103 | case aw_rtmp_state_connecting: 104 | break; 105 | } 106 | } 107 | 108 | static void aw_set_rtmp_state(aw_rtmp_context *ctx, aw_rtmp_state new_state){ 109 | if (ctx->rtmp_state == new_state) { 110 | return; 111 | } 112 | aw_rtmp_state old_state = ctx->rtmp_state; 113 | ctx->rtmp_state = new_state; 114 | 115 | //回调用户接口 116 | if (ctx->state_changed_cb) { 117 | ctx->state_changed_cb(old_state, new_state); 118 | } 119 | 120 | //内部处理 121 | aw_rtmp_state_changed_inner(ctx, old_state, new_state); 122 | } 123 | 124 | int8_t aw_is_rtmp_opened(aw_rtmp_context *ctx){ 125 | return ctx && ctx->rtmp != NULL; 126 | } 127 | 128 | int aw_rtmp_open(aw_rtmp_context *ctx){ 129 | if (aw_is_rtmp_opened(ctx) || ctx->rtmp_state != aw_rtmp_state_idle) { 130 | AWLog("[error] static_aw_rtmp is in use"); 131 | return 0; 132 | } 133 | if (strlen(ctx->rtmp_url) <= 0 || strlen(ctx->rtmp_url) > 255) { 134 | AWLog("[error ] aw rtmp setup url = %s\n", ctx->rtmp_url); 135 | return 0; 136 | } 137 | aw_set_rtmp_state(ctx, aw_rtmp_state_connecting); 138 | int recode = 0; 139 | ctx->rtmp = RTMP_Alloc(); 140 | RTMP_Init(ctx->rtmp); 141 | ctx->rtmp->Link.timeout = 1; 142 | if (!RTMP_SetupURL(ctx->rtmp, ctx->rtmp_url)) { 143 | AWLog("[error ] aw rtmp setup url = %s\n", ctx->rtmp_url); 144 | recode = -2; 145 | goto FAILED; 146 | } 147 | 148 | RTMP_EnableWrite(ctx->rtmp); 149 | 150 | RTMP_SetBufferMS(ctx->rtmp, 3 * 1000); 151 | 152 | if (!RTMP_Connect(ctx->rtmp, NULL)) { 153 | recode = -3; 154 | goto FAILED; 155 | } 156 | 157 | if (!RTMP_ConnectStream(ctx->rtmp, 0)) { 158 | recode = -4; 159 | goto FAILED; 160 | } 161 | aw_set_rtmp_state(ctx, aw_rtmp_state_opened); 162 | return 1; 163 | FAILED: 164 | aw_rtmp_close(ctx); 165 | aw_set_rtmp_state(ctx, aw_rtmp_state_error_open); 166 | return !recode; 167 | } 168 | 169 | int aw_rtmp_close(aw_rtmp_context *ctx){ 170 | AWLog("aw rtmp closing.......\n"); 171 | if (aw_is_rtmp_opened(ctx)) { 172 | signal(SIGPIPE, SIG_IGN); 173 | RTMP_Close(ctx->rtmp); 174 | RTMP_Free(ctx->rtmp); 175 | ctx->rtmp = NULL; 176 | AWLog("aw rtmp closed.......\n"); 177 | aw_set_rtmp_state(ctx, aw_rtmp_state_closed); 178 | } 179 | return 1; 180 | } 181 | 182 | int aw_rtmp_write(aw_rtmp_context *ctx, const char *buf, int size){ 183 | if (!aw_is_rtmp_opened(ctx)) { 184 | AWLog("[error] aw rtmp writing but rtmp is not open"); 185 | return 0; 186 | } 187 | signal(SIGPIPE, SIG_IGN); 188 | int write_ret = RTMP_Write(ctx->rtmp, buf, size); 189 | if (write_ret <= 0) { 190 | aw_set_rtmp_state(ctx, aw_rtmp_state_error_write); 191 | } 192 | return write_ret; 193 | } 194 | 195 | uint32_t aw_rtmp_time(){ 196 | return RTMP_GetTime(); 197 | } -------------------------------------------------------------------------------- /iOS/cCodes/aw_rtmp.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_rtmp.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 20/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_rtmp_h 10 | #define aw_rtmp_h 11 | 12 | #include "rtmp.h" 13 | #include "aw_alloc.h" 14 | #include 15 | 16 | typedef enum aw_rtmp_state{ 17 | aw_rtmp_state_idle,//默认情况 18 | aw_rtmp_state_connecting,//连接中 19 | aw_rtmp_state_opened,//打开 20 | aw_rtmp_state_closed,//关闭,发送后回到idle状态 21 | aw_rtmp_state_error_write,//写入失败,发送完毕回到open状态 22 | aw_rtmp_state_error_open,//打开失败,发送后回到idle 23 | 24 | aw_rtmp_state_error_net,//多次连接失败,网络错误 25 | }aw_rtmp_state; 26 | 27 | typedef void (*aw_rtmp_state_changed_cb) (aw_rtmp_state old_state, aw_rtmp_state new_state); 28 | 29 | typedef struct aw_rtmp_context{ 30 | char rtmp_url[256]; 31 | RTMP *rtmp; 32 | 33 | //写入错误断线重连 34 | int8_t write_error_retry_time_limit;//写入错误重试次数,超过这个次数就要重连。 35 | int8_t write_error_retry_curr_time;//当前重试错误次数。 36 | 37 | //open错误重连 38 | int8_t open_error_retry_time_limit;//连接重试次数,超过这个次数。 39 | int8_t open_error_retry_curr_time;//当前连接重试的次数,表示连接错误。 40 | 41 | int8_t is_header_sent;//是不是发送过flv的header了 42 | 43 | //当前mp4文件开始时间戳 44 | double current_time_stamp; 45 | double last_time_stamp; 46 | 47 | //总时间 48 | double total_duration; 49 | 50 | aw_rtmp_state_changed_cb state_changed_cb; 51 | aw_rtmp_state rtmp_state; 52 | } aw_rtmp_context; 53 | 54 | extern void aw_init_rtmp_context(aw_rtmp_context *ctx, const char *rtmp_url, aw_rtmp_state_changed_cb state_changed_cb); 55 | extern aw_rtmp_context *alloc_aw_rtmp_context(const char *rtmp_url, aw_rtmp_state_changed_cb state_changed_cb); 56 | extern void free_aw_rtmp_context(aw_rtmp_context **ctx); 57 | 58 | //rtmp是否打开 59 | extern int8_t aw_is_rtmp_opened(aw_rtmp_context *ctx); 60 | 61 | //打开rtmp 62 | extern int aw_rtmp_open(aw_rtmp_context *ctx); 63 | 64 | //写入数据 65 | extern int aw_rtmp_write(aw_rtmp_context *ctx, const char *buf, int size); 66 | 67 | //关闭rtmp 68 | extern int aw_rtmp_close(aw_rtmp_context *ctx); 69 | 70 | //获取当前时间 71 | extern uint32_t aw_rtmp_time(); 72 | 73 | #endif /* aw_rtmp_h */ 74 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_utils.c: -------------------------------------------------------------------------------- 1 | // 2 | // aw_utils.c 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 11/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #include "aw_utils.h" 10 | -------------------------------------------------------------------------------- /iOS/cCodes/aw_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // aw_utils.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 11/7/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #ifndef aw_utils_h 10 | #define aw_utils_h 11 | 12 | #include 13 | #include "aw_alloc.h" 14 | 15 | #define AWLog(...) \ 16 | do{ \ 17 | printf(__VA_ARGS__); \ 18 | printf("\n");\ 19 | }while(0) 20 | 21 | #endif /* aw_utils_h */ 22 | -------------------------------------------------------------------------------- /iOS/librtmp/include/amf.h: -------------------------------------------------------------------------------- 1 | #ifndef __AMF_H__ 2 | #define __AMF_H__ 3 | /* 4 | * Copyright (C) 2005-2008 Team XBMC 5 | * http://www.xbmc.org 6 | * Copyright (C) 2008-2009 Andrej Stepanchuk 7 | * Copyright (C) 2009-2010 Howard Chu 8 | * 9 | * This file is part of librtmp. 10 | * 11 | * librtmp is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU Lesser General Public License as 13 | * published by the Free Software Foundation; either version 2.1, 14 | * or (at your option) any later version. 15 | * 16 | * librtmp is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public License 22 | * along with librtmp see the file COPYING. If not, write to 23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301, USA. 25 | * http://www.gnu.org/copyleft/lgpl.html 26 | */ 27 | 28 | #include 29 | 30 | #ifndef TRUE 31 | #define TRUE 1 32 | #define FALSE 0 33 | #endif 34 | 35 | #ifdef __cplusplus 36 | extern "C" 37 | { 38 | #endif 39 | 40 | typedef enum 41 | { AMF_NUMBER = 0, AMF_BOOLEAN, AMF_STRING, AMF_OBJECT, 42 | AMF_MOVIECLIP, /* reserved, not used */ 43 | AMF_NULL, AMF_UNDEFINED, AMF_REFERENCE, AMF_ECMA_ARRAY, AMF_OBJECT_END, 44 | AMF_STRICT_ARRAY, AMF_DATE, AMF_LONG_STRING, AMF_UNSUPPORTED, 45 | AMF_RECORDSET, /* reserved, not used */ 46 | AMF_XML_DOC, AMF_TYPED_OBJECT, 47 | AMF_AVMPLUS, /* switch to AMF3 */ 48 | AMF_INVALID = 0xff 49 | } AMFDataType; 50 | 51 | typedef enum 52 | { AMF3_UNDEFINED = 0, AMF3_NULL, AMF3_FALSE, AMF3_TRUE, 53 | AMF3_INTEGER, AMF3_DOUBLE, AMF3_STRING, AMF3_XML_DOC, AMF3_DATE, 54 | AMF3_ARRAY, AMF3_OBJECT, AMF3_XML, AMF3_BYTE_ARRAY 55 | } AMF3DataType; 56 | 57 | typedef struct AVal 58 | { 59 | char *av_val; 60 | int av_len; 61 | } AVal; 62 | #define AVC(str) {str,sizeof(str)-1} 63 | #define AVMATCH(a1,a2) ((a1)->av_len == (a2)->av_len && !memcmp((a1)->av_val,(a2)->av_val,(a1)->av_len)) 64 | 65 | struct AMFObjectProperty; 66 | 67 | typedef struct AMFObject 68 | { 69 | int o_num; 70 | struct AMFObjectProperty *o_props; 71 | } AMFObject; 72 | 73 | typedef struct AMFObjectProperty 74 | { 75 | AVal p_name; 76 | AMFDataType p_type; 77 | union 78 | { 79 | double p_number; 80 | AVal p_aval; 81 | AMFObject p_object; 82 | } p_vu; 83 | int16_t p_UTCoffset; 84 | } AMFObjectProperty; 85 | 86 | char *AMF_EncodeString(char *output, char *outend, const AVal * str); 87 | char *AMF_EncodeNumber(char *output, char *outend, double dVal); 88 | char *AMF_EncodeInt16(char *output, char *outend, short nVal); 89 | char *AMF_EncodeInt24(char *output, char *outend, int nVal); 90 | char *AMF_EncodeInt32(char *output, char *outend, int nVal); 91 | char *AMF_EncodeBoolean(char *output, char *outend, int bVal); 92 | 93 | /* Shortcuts for AMFProp_Encode */ 94 | char *AMF_EncodeNamedString(char *output, char *outend, const AVal * name, const AVal * value); 95 | char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal * name, double dVal); 96 | char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal * name, int bVal); 97 | 98 | unsigned short AMF_DecodeInt16(const char *data); 99 | unsigned int AMF_DecodeInt24(const char *data); 100 | unsigned int AMF_DecodeInt32(const char *data); 101 | void AMF_DecodeString(const char *data, AVal * str); 102 | void AMF_DecodeLongString(const char *data, AVal * str); 103 | int AMF_DecodeBoolean(const char *data); 104 | double AMF_DecodeNumber(const char *data); 105 | 106 | char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd); 107 | char *AMF_EncodeEcmaArray(AMFObject *obj, char *pBuffer, char *pBufEnd); 108 | char *AMF_EncodeArray(AMFObject *obj, char *pBuffer, char *pBufEnd); 109 | 110 | int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize, 111 | int bDecodeName); 112 | int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize, 113 | int nArrayLen, int bDecodeName); 114 | int AMF3_Decode(AMFObject * obj, const char *pBuffer, int nSize, 115 | int bDecodeName); 116 | void AMF_Dump(AMFObject * obj); 117 | void AMF_Reset(AMFObject * obj); 118 | 119 | void AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop); 120 | int AMF_CountProp(AMFObject * obj); 121 | AMFObjectProperty *AMF_GetProp(AMFObject * obj, const AVal * name, 122 | int nIndex); 123 | 124 | AMFDataType AMFProp_GetType(AMFObjectProperty * prop); 125 | void AMFProp_SetNumber(AMFObjectProperty * prop, double dval); 126 | void AMFProp_SetBoolean(AMFObjectProperty * prop, int bflag); 127 | void AMFProp_SetString(AMFObjectProperty * prop, AVal * str); 128 | void AMFProp_SetObject(AMFObjectProperty * prop, AMFObject * obj); 129 | 130 | void AMFProp_GetName(AMFObjectProperty * prop, AVal * name); 131 | void AMFProp_SetName(AMFObjectProperty * prop, AVal * name); 132 | double AMFProp_GetNumber(AMFObjectProperty * prop); 133 | int AMFProp_GetBoolean(AMFObjectProperty * prop); 134 | void AMFProp_GetString(AMFObjectProperty * prop, AVal * str); 135 | void AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj); 136 | 137 | int AMFProp_IsValid(AMFObjectProperty * prop); 138 | 139 | char *AMFProp_Encode(AMFObjectProperty * prop, char *pBuffer, char *pBufEnd); 140 | int AMF3Prop_Decode(AMFObjectProperty * prop, const char *pBuffer, 141 | int nSize, int bDecodeName); 142 | int AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer, 143 | int nSize, int bDecodeName); 144 | 145 | void AMFProp_Dump(AMFObjectProperty * prop); 146 | void AMFProp_Reset(AMFObjectProperty * prop); 147 | 148 | typedef struct AMF3ClassDef 149 | { 150 | AVal cd_name; 151 | char cd_externalizable; 152 | char cd_dynamic; 153 | int cd_num; 154 | AVal *cd_props; 155 | } AMF3ClassDef; 156 | 157 | void AMF3CD_AddProp(AMF3ClassDef * cd, AVal * prop); 158 | AVal *AMF3CD_GetProp(AMF3ClassDef * cd, int idx); 159 | 160 | #ifdef __cplusplus 161 | } 162 | #endif 163 | 164 | #endif /* __AMF_H__ */ 165 | -------------------------------------------------------------------------------- /iOS/librtmp/include/http.h: -------------------------------------------------------------------------------- 1 | #ifndef __RTMP_HTTP_H__ 2 | #define __RTMP_HTTP_H__ 3 | /* 4 | * Copyright (C) 2010 Howard Chu 5 | * Copyright (C) 2010 Antti Ajanki 6 | * 7 | * This file is part of librtmp. 8 | * 9 | * librtmp is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1, 12 | * or (at your option) any later version. 13 | * 14 | * librtmp is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public License 20 | * along with librtmp see the file COPYING. If not, write to 21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | * Boston, MA 02110-1301, USA. 23 | * http://www.gnu.org/copyleft/lgpl.html 24 | */ 25 | 26 | typedef enum { 27 | HTTPRES_OK, /* result OK */ 28 | HTTPRES_OK_NOT_MODIFIED, /* not modified since last request */ 29 | HTTPRES_NOT_FOUND, /* not found */ 30 | HTTPRES_BAD_REQUEST, /* client error */ 31 | HTTPRES_SERVER_ERROR, /* server reported an error */ 32 | HTTPRES_REDIRECTED, /* resource has been moved */ 33 | HTTPRES_LOST_CONNECTION /* connection lost while waiting for data */ 34 | } HTTPResult; 35 | 36 | struct HTTP_ctx { 37 | char *date; 38 | int size; 39 | int status; 40 | void *data; 41 | }; 42 | 43 | typedef size_t (HTTP_read_callback)(void *ptr, size_t size, size_t nmemb, void *stream); 44 | 45 | HTTPResult HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /iOS/librtmp/include/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008-2009 Andrej Stepanchuk 3 | * Copyright (C) 2009-2010 Howard Chu 4 | * 5 | * This file is part of librtmp. 6 | * 7 | * librtmp is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as 9 | * published by the Free Software Foundation; either version 2.1, 10 | * or (at your option) any later version. 11 | * 12 | * librtmp is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with librtmp see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 | * Boston, MA 02110-1301, USA. 21 | * http://www.gnu.org/copyleft/lgpl.html 22 | */ 23 | 24 | #ifndef __RTMP_LOG_H__ 25 | #define __RTMP_LOG_H__ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | /* Enable this to get full debugging output */ 35 | /* #define _DEBUG */ 36 | 37 | #ifdef _DEBUG 38 | #undef NODEBUG 39 | #endif 40 | 41 | typedef enum 42 | { RTMP_LOGCRIT=0, RTMP_LOGERROR, RTMP_LOGWARNING, RTMP_LOGINFO, 43 | RTMP_LOGDEBUG, RTMP_LOGDEBUG2, RTMP_LOGALL 44 | } RTMP_LogLevel; 45 | 46 | extern RTMP_LogLevel RTMP_debuglevel; 47 | 48 | typedef void (RTMP_LogCallback)(int level, const char *fmt, va_list); 49 | void RTMP_LogSetCallback(RTMP_LogCallback *cb); 50 | void RTMP_LogSetOutput(FILE *file); 51 | #ifdef __GNUC__ 52 | void RTMP_LogPrintf(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2))); 53 | void RTMP_LogStatus(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2))); 54 | void RTMP_Log(int level, const char *format, ...) __attribute__ ((__format__ (__printf__, 2, 3))); 55 | #else 56 | void RTMP_LogPrintf(const char *format, ...); 57 | void RTMP_LogStatus(const char *format, ...); 58 | void RTMP_Log(int level, const char *format, ...); 59 | #endif 60 | void RTMP_LogHex(int level, const uint8_t *data, unsigned long len); 61 | void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len); 62 | void RTMP_LogSetLevel(RTMP_LogLevel lvl); 63 | RTMP_LogLevel RTMP_LogGetLevel(void); 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /iOS/librtmp/include/rtmp.h: -------------------------------------------------------------------------------- 1 | #ifndef __RTMP_H__ 2 | #define __RTMP_H__ 3 | /* 4 | * Copyright (C) 2005-2008 Team XBMC 5 | * http://www.xbmc.org 6 | * Copyright (C) 2008-2009 Andrej Stepanchuk 7 | * Copyright (C) 2009-2010 Howard Chu 8 | * 9 | * This file is part of librtmp. 10 | * 11 | * librtmp is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU Lesser General Public License as 13 | * published by the Free Software Foundation; either version 2.1, 14 | * or (at your option) any later version. 15 | * 16 | * librtmp is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public License 22 | * along with librtmp see the file COPYING. If not, write to 23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301, USA. 25 | * http://www.gnu.org/copyleft/lgpl.html 26 | */ 27 | 28 | #if !defined(NO_CRYPTO) && !defined(CRYPTO) 29 | #define CRYPTO 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include "amf.h" 37 | 38 | #ifdef __cplusplus 39 | extern "C" 40 | { 41 | #endif 42 | 43 | #define RTMP_LIB_VERSION 0x020300 /* 2.3 */ 44 | 45 | #define RTMP_FEATURE_HTTP 0x01 46 | #define RTMP_FEATURE_ENC 0x02 47 | #define RTMP_FEATURE_SSL 0x04 48 | #define RTMP_FEATURE_MFP 0x08 /* not yet supported */ 49 | #define RTMP_FEATURE_WRITE 0x10 /* publish, not play */ 50 | #define RTMP_FEATURE_HTTP2 0x20 /* server-side rtmpt */ 51 | 52 | #define RTMP_PROTOCOL_UNDEFINED -1 53 | #define RTMP_PROTOCOL_RTMP 0 54 | #define RTMP_PROTOCOL_RTMPE RTMP_FEATURE_ENC 55 | #define RTMP_PROTOCOL_RTMPT RTMP_FEATURE_HTTP 56 | #define RTMP_PROTOCOL_RTMPS RTMP_FEATURE_SSL 57 | #define RTMP_PROTOCOL_RTMPTE (RTMP_FEATURE_HTTP|RTMP_FEATURE_ENC) 58 | #define RTMP_PROTOCOL_RTMPTS (RTMP_FEATURE_HTTP|RTMP_FEATURE_SSL) 59 | #define RTMP_PROTOCOL_RTMFP RTMP_FEATURE_MFP 60 | 61 | #define RTMP_DEFAULT_CHUNKSIZE 128 62 | 63 | /* needs to fit largest number of bytes recv() may return */ 64 | #define RTMP_BUFFER_CACHE_SIZE (16*1024) 65 | 66 | #define RTMP_CHANNELS 65600 67 | 68 | extern const char RTMPProtocolStringsLower[][7]; 69 | extern const AVal RTMP_DefaultFlashVer; 70 | extern int RTMP_ctrlC; 71 | 72 | uint32_t RTMP_GetTime(void); 73 | 74 | /* RTMP_PACKET_TYPE_... 0x00 */ 75 | #define RTMP_PACKET_TYPE_CHUNK_SIZE 0x01 76 | /* RTMP_PACKET_TYPE_... 0x02 */ 77 | #define RTMP_PACKET_TYPE_BYTES_READ_REPORT 0x03 78 | #define RTMP_PACKET_TYPE_CONTROL 0x04 79 | #define RTMP_PACKET_TYPE_SERVER_BW 0x05 80 | #define RTMP_PACKET_TYPE_CLIENT_BW 0x06 81 | /* RTMP_PACKET_TYPE_... 0x07 */ 82 | #define RTMP_PACKET_TYPE_AUDIO 0x08 83 | #define RTMP_PACKET_TYPE_VIDEO 0x09 84 | /* RTMP_PACKET_TYPE_... 0x0A */ 85 | /* RTMP_PACKET_TYPE_... 0x0B */ 86 | /* RTMP_PACKET_TYPE_... 0x0C */ 87 | /* RTMP_PACKET_TYPE_... 0x0D */ 88 | /* RTMP_PACKET_TYPE_... 0x0E */ 89 | #define RTMP_PACKET_TYPE_FLEX_STREAM_SEND 0x0F 90 | #define RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT 0x10 91 | #define RTMP_PACKET_TYPE_FLEX_MESSAGE 0x11 92 | #define RTMP_PACKET_TYPE_INFO 0x12 93 | #define RTMP_PACKET_TYPE_SHARED_OBJECT 0x13 94 | #define RTMP_PACKET_TYPE_INVOKE 0x14 95 | /* RTMP_PACKET_TYPE_... 0x15 */ 96 | #define RTMP_PACKET_TYPE_FLASH_VIDEO 0x16 97 | 98 | #define RTMP_MAX_HEADER_SIZE 18 99 | 100 | #define RTMP_PACKET_SIZE_LARGE 0 101 | #define RTMP_PACKET_SIZE_MEDIUM 1 102 | #define RTMP_PACKET_SIZE_SMALL 2 103 | #define RTMP_PACKET_SIZE_MINIMUM 3 104 | 105 | typedef struct RTMPChunk 106 | { 107 | int c_headerSize; 108 | int c_chunkSize; 109 | char *c_chunk; 110 | char c_header[RTMP_MAX_HEADER_SIZE]; 111 | } RTMPChunk; 112 | 113 | typedef struct RTMPPacket 114 | { 115 | uint8_t m_headerType; 116 | uint8_t m_packetType; 117 | uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */ 118 | int m_nChannel; 119 | uint32_t m_nTimeStamp; /* timestamp */ 120 | int32_t m_nInfoField2; /* last 4 bytes in a long header */ 121 | uint32_t m_nBodySize; 122 | uint32_t m_nBytesRead; 123 | RTMPChunk *m_chunk; 124 | char *m_body; 125 | } RTMPPacket; 126 | 127 | typedef struct RTMPSockBuf 128 | { 129 | int sb_socket; 130 | int sb_size; /* number of unprocessed bytes in buffer */ 131 | char *sb_start; /* pointer into sb_pBuffer of next byte to process */ 132 | char sb_buf[RTMP_BUFFER_CACHE_SIZE]; /* data read from socket */ 133 | int sb_timedout; 134 | void *sb_ssl; 135 | } RTMPSockBuf; 136 | 137 | void RTMPPacket_Reset(RTMPPacket *p); 138 | void RTMPPacket_Dump(RTMPPacket *p); 139 | int RTMPPacket_Alloc(RTMPPacket *p, int nSize); 140 | void RTMPPacket_Free(RTMPPacket *p); 141 | 142 | #define RTMPPacket_IsReady(a) ((a)->m_nBytesRead == (a)->m_nBodySize) 143 | 144 | typedef struct RTMP_LNK 145 | { 146 | AVal hostname; 147 | AVal sockshost; 148 | 149 | AVal playpath0; /* parsed from URL */ 150 | AVal playpath; /* passed in explicitly */ 151 | AVal tcUrl; 152 | AVal swfUrl; 153 | AVal pageUrl; 154 | AVal app; 155 | AVal auth; 156 | AVal flashVer; 157 | AVal subscribepath; 158 | AVal usherToken; 159 | AVal token; 160 | AVal pubUser; 161 | AVal pubPasswd; 162 | AMFObject extras; 163 | int edepth; 164 | 165 | int seekTime; 166 | int stopTime; 167 | 168 | #define RTMP_LF_AUTH 0x0001 /* using auth param */ 169 | #define RTMP_LF_LIVE 0x0002 /* stream is live */ 170 | #define RTMP_LF_SWFV 0x0004 /* do SWF verification */ 171 | #define RTMP_LF_PLST 0x0008 /* send playlist before play */ 172 | #define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */ 173 | #define RTMP_LF_FTCU 0x0020 /* free tcUrl on close */ 174 | #define RTMP_LF_FAPU 0x0040 /* free app on close */ 175 | int lFlags; 176 | 177 | int swfAge; 178 | 179 | int protocol; 180 | int timeout; /* connection timeout in seconds */ 181 | 182 | int pFlags; /* unused, but kept to avoid breaking ABI */ 183 | 184 | unsigned short socksport; 185 | unsigned short port; 186 | 187 | #ifdef CRYPTO 188 | #define RTMP_SWF_HASHLEN 32 189 | void *dh; /* for encryption */ 190 | void *rc4keyIn; 191 | void *rc4keyOut; 192 | 193 | uint32_t SWFSize; 194 | uint8_t SWFHash[RTMP_SWF_HASHLEN]; 195 | char SWFVerificationResponse[RTMP_SWF_HASHLEN+10]; 196 | #endif 197 | } RTMP_LNK; 198 | 199 | /* state for read() wrapper */ 200 | typedef struct RTMP_READ 201 | { 202 | char *buf; 203 | char *bufpos; 204 | unsigned int buflen; 205 | uint32_t timestamp; 206 | uint8_t dataType; 207 | uint8_t flags; 208 | #define RTMP_READ_HEADER 0x01 209 | #define RTMP_READ_RESUME 0x02 210 | #define RTMP_READ_NO_IGNORE 0x04 211 | #define RTMP_READ_GOTKF 0x08 212 | #define RTMP_READ_GOTFLVK 0x10 213 | #define RTMP_READ_SEEKING 0x20 214 | int8_t status; 215 | #define RTMP_READ_COMPLETE -3 216 | #define RTMP_READ_ERROR -2 217 | #define RTMP_READ_EOF -1 218 | #define RTMP_READ_IGNORE 0 219 | 220 | /* if bResume == TRUE */ 221 | uint8_t initialFrameType; 222 | uint32_t nResumeTS; 223 | char *metaHeader; 224 | char *initialFrame; 225 | uint32_t nMetaHeaderSize; 226 | uint32_t nInitialFrameSize; 227 | uint32_t nIgnoredFrameCounter; 228 | uint32_t nIgnoredFlvFrameCounter; 229 | } RTMP_READ; 230 | 231 | typedef struct RTMP_METHOD 232 | { 233 | AVal name; 234 | int num; 235 | } RTMP_METHOD; 236 | 237 | typedef struct RTMP 238 | { 239 | int m_inChunkSize; 240 | int m_outChunkSize; 241 | int m_nBWCheckCounter; 242 | int m_nBytesIn; 243 | int m_nBytesInSent; 244 | int m_nBufferMS; 245 | int m_stream_id; /* returned in _result from createStream */ 246 | int m_mediaChannel; 247 | uint32_t m_mediaStamp; 248 | uint32_t m_pauseStamp; 249 | int m_pausing; 250 | int m_nServerBW; 251 | int m_nClientBW; 252 | uint8_t m_nClientBW2; 253 | uint8_t m_bPlaying; 254 | uint8_t m_bSendEncoding; 255 | uint8_t m_bSendCounter; 256 | 257 | int m_numInvokes; 258 | int m_numCalls; 259 | RTMP_METHOD *m_methodCalls; /* remote method calls queue */ 260 | 261 | int m_channelsAllocatedIn; 262 | int m_channelsAllocatedOut; 263 | RTMPPacket **m_vecChannelsIn; 264 | RTMPPacket **m_vecChannelsOut; 265 | int *m_channelTimestamp; /* abs timestamp of last packet */ 266 | 267 | double m_fAudioCodecs; /* audioCodecs for the connect packet */ 268 | double m_fVideoCodecs; /* videoCodecs for the connect packet */ 269 | double m_fEncoding; /* AMF0 or AMF3 */ 270 | 271 | double m_fDuration; /* duration of stream in seconds */ 272 | 273 | int m_msgCounter; /* RTMPT stuff */ 274 | int m_polling; 275 | int m_resplen; 276 | int m_unackd; 277 | AVal m_clientID; 278 | 279 | RTMP_READ m_read; 280 | RTMPPacket m_write; 281 | RTMPSockBuf m_sb; 282 | RTMP_LNK Link; 283 | } RTMP; 284 | 285 | int RTMP_ParseURL(const char *url, int *protocol, AVal *host, 286 | unsigned int *port, AVal *playpath, AVal *app); 287 | 288 | void RTMP_ParsePlaypath(AVal *in, AVal *out); 289 | void RTMP_SetBufferMS(RTMP *r, int size); 290 | void RTMP_UpdateBufferMS(RTMP *r); 291 | 292 | int RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg); 293 | int RTMP_SetupURL(RTMP *r, char *url); 294 | void RTMP_SetupStream(RTMP *r, int protocol, 295 | AVal *hostname, 296 | unsigned int port, 297 | AVal *sockshost, 298 | AVal *playpath, 299 | AVal *tcUrl, 300 | AVal *swfUrl, 301 | AVal *pageUrl, 302 | AVal *app, 303 | AVal *auth, 304 | AVal *swfSHA256Hash, 305 | uint32_t swfSize, 306 | AVal *flashVer, 307 | AVal *subscribepath, 308 | AVal *usherToken, 309 | int dStart, 310 | int dStop, int bLiveStream, long int timeout); 311 | 312 | int RTMP_Connect(RTMP *r, RTMPPacket *cp); 313 | struct sockaddr; 314 | int RTMP_Connect0(RTMP *r, struct sockaddr *svc); 315 | int RTMP_Connect1(RTMP *r, RTMPPacket *cp); 316 | int RTMP_Serve(RTMP *r); 317 | int RTMP_TLS_Accept(RTMP *r, void *ctx); 318 | 319 | int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet); 320 | int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue); 321 | int RTMP_SendChunk(RTMP *r, RTMPChunk *chunk); 322 | int RTMP_IsConnected(RTMP *r); 323 | int RTMP_Socket(RTMP *r); 324 | int RTMP_IsTimedout(RTMP *r); 325 | double RTMP_GetDuration(RTMP *r); 326 | int RTMP_ToggleStream(RTMP *r); 327 | 328 | int RTMP_ConnectStream(RTMP *r, int seekTime); 329 | int RTMP_ReconnectStream(RTMP *r, int seekTime); 330 | void RTMP_DeleteStream(RTMP *r); 331 | int RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet); 332 | int RTMP_ClientPacket(RTMP *r, RTMPPacket *packet); 333 | 334 | void RTMP_Init(RTMP *r); 335 | void RTMP_Close(RTMP *r); 336 | RTMP *RTMP_Alloc(void); 337 | void RTMP_Free(RTMP *r); 338 | void RTMP_EnableWrite(RTMP *r); 339 | 340 | void *RTMP_TLS_AllocServerContext(const char* cert, const char* key); 341 | void RTMP_TLS_FreeServerContext(void *ctx); 342 | 343 | int RTMP_LibVersion(void); 344 | void RTMP_UserInterrupt(void); /* user typed Ctrl-C */ 345 | 346 | int RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, 347 | unsigned int nTime); 348 | 349 | /* caller probably doesn't know current timestamp, should 350 | * just use RTMP_Pause instead 351 | */ 352 | int RTMP_SendPause(RTMP *r, int DoPause, int dTime); 353 | int RTMP_Pause(RTMP *r, int DoPause); 354 | 355 | int RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name, 356 | AMFObjectProperty * p); 357 | 358 | int RTMPSockBuf_Fill(RTMPSockBuf *sb); 359 | int RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len); 360 | int RTMPSockBuf_Close(RTMPSockBuf *sb); 361 | 362 | int RTMP_SendCreateStream(RTMP *r); 363 | int RTMP_SendSeek(RTMP *r, int dTime); 364 | int RTMP_SendServerBW(RTMP *r); 365 | int RTMP_SendClientBW(RTMP *r); 366 | void RTMP_DropRequest(RTMP *r, int i, int freeit); 367 | int RTMP_Read(RTMP *r, char *buf, int size); 368 | int RTMP_Write(RTMP *r, const char *buf, int size); 369 | 370 | /* hashswf.c */ 371 | int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, 372 | int age); 373 | 374 | #ifdef __cplusplus 375 | }; 376 | #endif 377 | 378 | #endif 379 | -------------------------------------------------------------------------------- /iOS/librtmp/lib/librtmp.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hardman/ios_push_stream_with_mp4_hard_encode/8bd2e0214e6bfaab95156f4f6f817d8b1ff62b36/iOS/librtmp/lib/librtmp.a -------------------------------------------------------------------------------- /iOS/pushStreamInC.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/AWVideoCapture.h: -------------------------------------------------------------------------------- 1 | // 2 | // AWVideoCapture.h 3 | // TestRecordVideo 4 | // 5 | // Created by kaso on 23/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "AWVideoMp4File.h" 12 | #include "aw_pushstream.h" 13 | 14 | @class AWVideoCapture; 15 | @protocol AWVideoCaptureDelegate 16 | -(void) videoCapture:(AWVideoCapture *)capture stateChangeFrom:(aw_rtmp_state) fromState toState:(aw_rtmp_state) toState; 17 | @end 18 | 19 | @interface AWVideoCapture : NSObject 20 | //状态变化回调 21 | @property (nonatomic, weak) id delegate; 22 | 23 | //是否将数据收集起来 24 | @property (nonatomic, readonly, unsafe_unretained) BOOL isCapturing; 25 | 26 | //预览view 27 | @property (nonatomic, readonly, strong) UIView *preview; 28 | 29 | //VideoFile 30 | @property (nonatomic, readonly, strong) AWVideoMp4File *videoMp4File; 31 | 32 | @property (nonatomic, copy) NSString *rtmpUrl; 33 | 34 | //切换摄像头 35 | -(void) switchCamera; 36 | 37 | //停止capture 38 | -(void) stopCapture; 39 | 40 | //开始capture 41 | -(BOOL) startCapture; 42 | 43 | //开始capture 44 | -(BOOL) startCaptureWithCMTime:(CMTime) time; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/AWVideoCapture.m: -------------------------------------------------------------------------------- 1 | // 2 | // AWVideoCapture.m 3 | // TestRecordVideo 4 | // 5 | // Created by kaso on 23/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import "AWVideoCapture.h" 10 | #import 11 | #import 12 | #import "AWVideoFifoList.h" 13 | #include "aw_pushstream.h" 14 | 15 | //保存sample数据 16 | @interface AWVideoFrame : NSObject 17 | @property (nonatomic, unsafe_unretained) CMSampleBufferRef sampleBuff; 18 | @property (nonatomic, unsafe_unretained) BOOL isVideo; 19 | @end 20 | 21 | @implementation AWVideoFrame 22 | @end 23 | 24 | @interface AWVideoCapture () 25 | //前后摄像头 26 | @property (nonatomic, strong) AVCaptureDeviceInput *frontCamera; 27 | @property (nonatomic, strong) AVCaptureDeviceInput *backCamera; 28 | //当前使用的视频设备 29 | @property (nonatomic, weak) AVCaptureDeviceInput *videoInputDevice; 30 | 31 | //麦克风 32 | @property (nonatomic, strong) AVCaptureDeviceInput *audioInputDevice; 33 | 34 | //输出文件 35 | @property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput; 36 | @property (nonatomic, strong) AVCaptureAudioDataOutput *audioDataOutput; 37 | 38 | //会话 39 | @property (nonatomic, strong) AVCaptureSession *captureSession; 40 | 41 | //预览 42 | @property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer; 43 | //预览结果view 44 | @property (nonatomic, strong) UIView *preview; 45 | 46 | //保存sampleList 47 | @property (nonatomic, strong) AWVideoFifoList *sampleList; 48 | 49 | //VideoFile 50 | @property (nonatomic, strong) AWVideoMp4File *videoMp4File; 51 | 52 | //处理videFile 53 | @property (nonatomic, strong) dispatch_source_t videoFileSource; 54 | 55 | //是否正在capture 56 | @property (nonatomic, unsafe_unretained) BOOL isCapturing; 57 | @end 58 | 59 | __weak static AWVideoCapture *sAwVideoCapture = nil; 60 | 61 | @implementation AWVideoCapture 62 | 63 | //属性实现 64 | -(AWVideoFifoList *)sampleList{ 65 | if (!_sampleList) { 66 | _sampleList = [AWVideoFifoList new]; 67 | } 68 | return _sampleList; 69 | } 70 | 71 | -(AWVideoMp4File *)videoMp4File{ 72 | if (!_videoMp4File) { 73 | _videoMp4File = [AWVideoMp4File new]; 74 | } 75 | return _videoMp4File; 76 | } 77 | 78 | -(void)setisCapturing:(BOOL)isCapturing{ 79 | if (_isCapturing == isCapturing) { 80 | return; 81 | } 82 | 83 | if (!isCapturing) { 84 | [self onPauseCapture]; 85 | }else{ 86 | [self onStartCapture]; 87 | } 88 | 89 | _isCapturing = isCapturing; 90 | } 91 | 92 | -(void)switchCamera{ 93 | if ([self.videoInputDevice isEqual: self.frontCamera]) { 94 | self.videoInputDevice = self.backCamera; 95 | }else{ 96 | self.videoInputDevice = self.frontCamera; 97 | } 98 | } 99 | 100 | -(UIView *)preview{ 101 | if (!_preview) { 102 | _preview = [UIView new]; 103 | _preview.bounds = [UIScreen mainScreen].bounds; 104 | } 105 | return _preview; 106 | } 107 | 108 | -(dispatch_source_t)videoFileSource{ 109 | if (!_videoFileSource) { 110 | [self createVideoFileSource]; 111 | } 112 | return _videoFileSource; 113 | } 114 | 115 | - (instancetype)init 116 | { 117 | self = [super init]; 118 | if (self) { 119 | [self createCaptureDevice]; 120 | [self createCaptureSession]; 121 | [self createPreviewLayer]; 122 | sAwVideoCapture = self; 123 | } 124 | return self; 125 | } 126 | 127 | //初始化视频设备 128 | -(void) createCaptureDevice{ 129 | //创建视频设备 130 | NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; 131 | //初始化摄像头 132 | self.frontCamera = [AVCaptureDeviceInput deviceInputWithDevice:videoDevices.firstObject error:nil]; 133 | self.backCamera =[AVCaptureDeviceInput deviceInputWithDevice:videoDevices.lastObject error:nil]; 134 | 135 | //麦克风 136 | AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; 137 | self.audioInputDevice = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil]; 138 | 139 | [self createOutput]; 140 | 141 | self.videoInputDevice = self.frontCamera; 142 | } 143 | 144 | //切换摄像头 145 | -(void)setVideoInputDevice:(AVCaptureDeviceInput *)videoInputDevice{ 146 | //modifyinput 147 | [self.captureSession beginConfiguration]; 148 | if (_videoInputDevice) { 149 | [self.captureSession removeInput:_videoInputDevice]; 150 | } 151 | if (videoInputDevice) { 152 | [self.captureSession addInput:videoInputDevice]; 153 | } 154 | 155 | [self setVideoOutConfig]; 156 | 157 | [self.captureSession commitConfiguration]; 158 | 159 | _videoInputDevice = videoInputDevice; 160 | } 161 | 162 | //创建预览 163 | -(void) createPreviewLayer{ 164 | self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession]; 165 | self.previewLayer.frame = self.preview.bounds; 166 | [self.preview.layer addSublayer:self.previewLayer]; 167 | } 168 | 169 | -(void) setVideoOutConfig{ 170 | for (AVCaptureConnection *conn in self.videoDataOutput.connections) { 171 | if (conn.isVideoStabilizationSupported) { 172 | [conn setPreferredVideoStabilizationMode:AVCaptureVideoStabilizationModeAuto]; 173 | } 174 | if (conn.isVideoOrientationSupported) { 175 | [conn setVideoOrientation:AVCaptureVideoOrientationPortrait]; 176 | } 177 | if (conn.isVideoMirrored) { 178 | [conn setVideoMirrored: YES]; 179 | } 180 | } 181 | } 182 | 183 | //创建会话 184 | -(void) createCaptureSession{ 185 | self.captureSession = [AVCaptureSession new]; 186 | 187 | [self.captureSession beginConfiguration]; 188 | 189 | if ([self.captureSession canAddInput:self.videoInputDevice]) { 190 | [self.captureSession addInput:self.videoInputDevice]; 191 | } 192 | 193 | if ([self.captureSession canAddInput:self.audioInputDevice]) { 194 | [self.captureSession addInput:self.audioInputDevice]; 195 | } 196 | 197 | if([self.captureSession canAddOutput:self.videoDataOutput]){ 198 | [self.captureSession addOutput:self.videoDataOutput]; 199 | [self setVideoOutConfig]; 200 | } 201 | 202 | if([self.captureSession canAddOutput:self.audioDataOutput]){ 203 | [self.captureSession addOutput:self.audioDataOutput]; 204 | } 205 | 206 | self.captureSession.sessionPreset = AVCaptureSessionPresetHigh; 207 | 208 | [self.captureSession commitConfiguration]; 209 | 210 | [self.captureSession startRunning]; 211 | } 212 | 213 | //销毁会话 214 | -(void) destroyCaptureSession{ 215 | if (self.captureSession) { 216 | [self.captureSession removeInput:self.audioInputDevice]; 217 | [self.captureSession removeInput:self.videoInputDevice]; 218 | [self.captureSession removeOutput:self.self.videoDataOutput]; 219 | [self.captureSession removeOutput:self.self.audioDataOutput]; 220 | } 221 | self.captureSession = nil; 222 | } 223 | 224 | -(void) createOutput{ 225 | self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; 226 | [self.videoDataOutput setSampleBufferDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; 227 | [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; 228 | [self.videoDataOutput setVideoSettings:@{ 229 | (__bridge NSString *)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) 230 | }]; 231 | 232 | self.audioDataOutput = [[AVCaptureAudioDataOutput alloc] init]; 233 | [self.audioDataOutput setSampleBufferDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; 234 | } 235 | 236 | -(void) createVideoFileSource{ 237 | dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); 238 | dispatch_source_set_event_handler(source, ^{ 239 | @synchronized (_videoFileSource) { 240 | while (!self.sampleList.empty) { 241 | AWVideoFrame *frame = self.sampleList.popElement; 242 | if (!self.videoMp4File.isWriting) { 243 | if([self.videoMp4File startWritingWithTime:CMSampleBufferGetPresentationTimeStamp(frame.sampleBuff)]){ 244 | NSLog(@"start writing with time is succ!!!"); 245 | }else{ 246 | NSLog(@"start writing with time is Error!!!"); 247 | } 248 | } 249 | if (self.videoMp4File.isWriting) { 250 | BOOL isSucc = NO; 251 | if (frame.isVideo) { 252 | isSucc = [self.videoMp4File writeVideoSample:frame.sampleBuff]; 253 | }else{ 254 | isSucc = [self.videoMp4File writeAudioSample:frame.sampleBuff]; 255 | } 256 | if (isSucc) { 257 | CFRelease(frame.sampleBuff); 258 | }else{ 259 | //这么处理可能会丢帧,但是并不影响,最多少2秒 260 | [self.sampleList addElementToHeader:frame]; 261 | [self.videoMp4File newFileAssetWriterWithOutSaveFile]; 262 | break; 263 | } 264 | }else{ 265 | [self.sampleList addElementToHeader:frame]; 266 | [self.videoMp4File newFileAssetWriterWithOutSaveFile]; 267 | } 268 | } 269 | } 270 | }); 271 | 272 | _videoFileSource = source; 273 | 274 | [self resumeVideoFileSource]; 275 | } 276 | 277 | -(void) resumeVideoFileSource{ 278 | if (_videoFileSource) { 279 | dispatch_resume(_videoFileSource); 280 | } 281 | } 282 | 283 | -(void) pauseVideoFileSource{ 284 | if (_videoFileSource) { 285 | dispatch_suspend(_videoFileSource); 286 | } 287 | } 288 | 289 | -(void) stopVideoFileSource{ 290 | if (_videoFileSource) { 291 | dispatch_source_cancel(_videoFileSource); 292 | } 293 | } 294 | 295 | -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{ 296 | if (self.isCapturing) { 297 | AWVideoFrame *frame = [AWVideoFrame new]; 298 | frame.sampleBuff = sampleBuffer; 299 | if ([self.videoDataOutput isEqual:captureOutput]) { 300 | frame.isVideo = YES; 301 | }else if([self.audioDataOutput isEqual:captureOutput]){ 302 | frame.isVideo = NO; 303 | }else{ 304 | NSLog(@"!!!!!!!!!!! is not video and not audio "); 305 | } 306 | 307 | CFRetain(frame.sampleBuff); 308 | [self.sampleList addElement:frame]; 309 | 310 | dispatch_source_merge_data(self.videoFileSource, 1); 311 | } 312 | } 313 | 314 | -(void) onPauseCapture{ 315 | [self pauseVideoFileSource]; 316 | [self.sampleList clean]; 317 | } 318 | 319 | -(void) onStartCapture{ 320 | [self resumeVideoFileSource]; 321 | } 322 | 323 | static void aw_rtmp_state_changed_cb_in_oc(aw_rtmp_state old_state, aw_rtmp_state new_state){ 324 | NSLog(@"[OC] rtmp state changed from(%d), to(%d)", old_state, new_state); 325 | [sAwVideoCapture.delegate videoCapture:sAwVideoCapture stateChangeFrom:old_state toState:new_state]; 326 | } 327 | 328 | -(BOOL) startCapture{ 329 | if (!self.rtmpUrl || self.rtmpUrl.length < 8) { 330 | NSLog(@"rtmpUrl is nil when start capture"); 331 | return NO; 332 | } 333 | int retcode = aw_open_rtmp_context_for_parsed_mp4(self.rtmpUrl.UTF8String, aw_rtmp_state_changed_cb_in_oc); 334 | if(retcode){ 335 | self.isCapturing = YES; 336 | }else{ 337 | NSLog(@"startCapture rtmpOpen error!!! retcode=%d", retcode); 338 | return NO; 339 | } 340 | return YES; 341 | } 342 | 343 | -(void) stopCapture{ 344 | self.isCapturing = NO; 345 | [self.videoMp4File stopWritingWithFinishHandler:^{ 346 | NSLog(@"capture stoped"); 347 | }]; 348 | aw_close_rtmp_context_for_parsed_mp4(); 349 | } 350 | 351 | -(BOOL) startCaptureWithCMTime:(CMTime) time{ 352 | if([self.videoMp4File startWritingWithTime:time]){ 353 | self.isCapturing = YES; 354 | return YES; 355 | } 356 | return NO; 357 | } 358 | 359 | @end 360 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/AWVideoFifoList.h: -------------------------------------------------------------------------------- 1 | // 2 | // AWVideoFifoList.h 3 | // TestRecordVideo 4 | // 5 | // Created by kaso on 23/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import 10 | @interface AWVideoFifoList : NSObject 11 | //数量限制 12 | @property (nonatomic, unsafe_unretained) NSInteger eleLimit; 13 | -(void) addElement:(id)ele; 14 | -(id) popElement; 15 | -(void) addElementToHeader:(id)ele; 16 | -(void) clean; 17 | -(BOOL) empty; 18 | -(NSInteger) count; 19 | @end 20 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/AWVideoFifoList.m: -------------------------------------------------------------------------------- 1 | // 2 | // AWVideoFifoList.m 3 | // TestRecordVideo 4 | // 5 | // Created by kaso on 23/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import "AWVideoFifoList.h" 10 | 11 | @interface AWVideoFifoList () 12 | @property (nonatomic, strong) NSMutableArray *mutableArray; 13 | @end 14 | 15 | @implementation AWVideoFifoList 16 | 17 | -(NSMutableArray *)mutableArray{ 18 | if (!_mutableArray) { 19 | _mutableArray = [NSMutableArray new]; 20 | } 21 | return _mutableArray; 22 | } 23 | 24 | -(NSInteger)eleLimit{ 25 | if (!_eleLimit) { 26 | _eleLimit = 8 * 25; 27 | } 28 | return _eleLimit; 29 | } 30 | 31 | -(void) addElement:(id)ele{ 32 | @synchronized (self) { 33 | if (self.mutableArray.count >= self.eleLimit) { 34 | NSLog(@"drop one frame!!!!"); 35 | [self popElement]; 36 | } 37 | [self.mutableArray addObject:ele]; 38 | } 39 | } 40 | 41 | -(void) addElementToHeader:(id)ele{ 42 | [self.mutableArray insertObject:ele atIndex:0]; 43 | } 44 | 45 | -(id) popElement{ 46 | id ret = nil; 47 | @synchronized (self) { 48 | ret = self.mutableArray.firstObject; 49 | [self.mutableArray removeObjectAtIndex:0]; 50 | } 51 | return ret; 52 | } 53 | 54 | -(void) clean{ 55 | @synchronized (self) { 56 | self.mutableArray = nil; 57 | } 58 | } 59 | 60 | -(BOOL) empty{ 61 | return self.mutableArray.count == 0; 62 | } 63 | 64 | -(NSInteger) count{ 65 | return self.mutableArray.count; 66 | } 67 | 68 | -(NSString *)description{ 69 | return _mutableArray.description; 70 | } 71 | 72 | @end -------------------------------------------------------------------------------- /iOS/pushStreamInC/AWVideoFlvStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // VideoFlvStreamVideoFlvStream.h 3 | // TestRecordVideo 4 | // 5 | // Created by kaso on 23/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AWVideoMp4File.h" 11 | #import "AWVideoFifoList.h" 12 | 13 | @interface AWVideoFlvStream : NSObject 14 | @property (nonatomic, copy) NSString *rtmpUrl; 15 | -(void) notifyMp4FileListChanged:(AWVideoFifoList *)list; 16 | -(void) startStream; 17 | -(void) stopStream; 18 | @end 19 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/AWVideoFlvStream.m: -------------------------------------------------------------------------------- 1 | // 2 | // VideoFlvStreamVideoFlvStream.m 3 | // TestRecordVideo 4 | // 5 | // Created by kaso on 23/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import "AWVideoFlvStream.h" 10 | #import 11 | #include "aw_pushstream.h" 12 | 13 | @interface AWVideoFlvStream () 14 | //是否已经发送了flv header 15 | @property (nonatomic, unsafe_unretained) BOOL isHeaderSend; 16 | //保证不被调用多次 17 | @property (atomic, unsafe_unretained) BOOL isProcessing; 18 | 19 | @property (atomic, unsafe_unretained) BOOL isStreaming; 20 | 21 | @property (nonatomic, unsafe_unretained) uint32_t startTime; 22 | 23 | @property (nonatomic, unsafe_unretained) double_t currentTime; 24 | 25 | @property (nonatomic, unsafe_unretained) double_t lastVideoTimeStamp; 26 | 27 | @property (nonatomic, unsafe_unretained) double_t lastAudioTimeStamp; 28 | @end 29 | 30 | @implementation AWVideoFlvStream 31 | 32 | -(void) notifyMp4FileListChanged:(AWVideoFifoList *)list{ 33 | if (!self.isStreaming) { 34 | NSLog(@"notifyMp4FileListChanged VideoFlvSteam is not streaming"); 35 | return; 36 | } 37 | if (self.isProcessing) { 38 | NSLog(@"AWVideoFlvStream notifyMp4FileListChanged isProcessing!!!"); 39 | return; 40 | } 41 | self.isProcessing = YES; 42 | NSLog(@" -- notify mp4 file list changed list.count=%ld", list.count); 43 | while (!list.empty) { 44 | [self handleOneMp4File:list.popElement]; 45 | } 46 | self.isProcessing = NO; 47 | } 48 | 49 | -(void) handleOneMp4File:(NSString *)mp4File{ 50 | NSLog(@"=== handleOneMp4File mp4File=%@", mp4File); 51 | //解析mp4文件 52 | 53 | [self parseMp4AndSendFlvStream:mp4File]; 54 | 55 | //移除处理过的文件 56 | [[NSFileManager defaultManager] removeItemAtPath:mp4File error:nil]; 57 | } 58 | 59 | -(void) parseMp4AndSendFlvStream:(NSString *)mp4File{ 60 | NSData *mp4FileData = [NSData dataWithContentsOfFile:mp4File]; 61 | aw_convert_parsed_mp4_to_flv_stream(mp4FileData.bytes, mp4FileData.length); 62 | } 63 | 64 | //开始发送流 65 | -(void) startStream{ 66 | self.isStreaming = YES; 67 | } 68 | 69 | //停止发送流 70 | -(void)stopStream{ 71 | self.isStreaming = NO; 72 | self.isHeaderSend = NO; 73 | } 74 | @end 75 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/AWVideoMp4File.h: -------------------------------------------------------------------------------- 1 | // 2 | // AWVideoMp4File.h 3 | // TestRecordVideo 4 | // 5 | // Created by kaso on 23/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface AWVideoMp4File : NSObject 13 | @property (nonatomic, readonly, unsafe_unretained) BOOL isWriting; 14 | //视频属性: 15 | @property (nonatomic, unsafe_unretained) NSInteger vWidth; 16 | @property (nonatomic, unsafe_unretained) NSInteger vHeight; 17 | @property (nonatomic, copy) NSString *vCodec; 18 | @property (nonatomic, unsafe_unretained) NSInteger vBitRate; 19 | @property (nonatomic, unsafe_unretained) NSInteger vMaxFrames; 20 | //音频属性 21 | @property (nonatomic, unsafe_unretained) NSInteger aCodec; 22 | @property (nonatomic, unsafe_unretained) CGFloat aSampleRate; 23 | @property (nonatomic, unsafe_unretained) NSInteger aBitRate; 24 | @property (nonatomic, unsafe_unretained) NSInteger aChanlesNum; 25 | @property (nonatomic, copy) NSData *aChannelLayoutData; 26 | //视频写入 27 | @property (nonatomic, readonly, strong) AVAssetWriter *fileAssetWriter; 28 | //video 输入 29 | @property (nonatomic, readonly, strong) AVAssetWriterInput *videoAssetWriterInput; 30 | //audio 输入 31 | @property (nonatomic, readonly, strong) AVAssetWriterInput *audioAssetWriterInput; 32 | 33 | -(BOOL) startWritingWithTime:(CMTime) time; 34 | 35 | -(void) stopWritingWithFinishHandler:(void(^)())finishHandler; 36 | 37 | -(BOOL) writeAudioSample:(CMSampleBufferRef) audioSample; 38 | -(BOOL) writeVideoSample:(CMSampleBufferRef) videoSample; 39 | 40 | //当前文件路径 41 | @property (nonatomic, readonly, copy) NSString *currentFilePath; 42 | 43 | -(NSString *)defaultPath; 44 | 45 | //重建一个新的fileAssetWriter 46 | -(void) newFileAssetWriter:(void (^)())preFileFinishHandler; 47 | 48 | -(void) newFileAssetWriterWithOutSaveFile; 49 | 50 | -(void)clean; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/AWVideoMp4File.m: -------------------------------------------------------------------------------- 1 | // 2 | // AWVideoMp4File.m 3 | // TestRecordVideo 4 | // 5 | // Created by kaso on 23/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import "AWVideoMp4File.h" 10 | #import "AWVideoFifoList.h" 11 | #import 12 | #import "AWVideoFlvStream.h" 13 | 14 | @interface AWVideoMp4File () 15 | //视频写入 16 | @property (nonatomic, strong) AVAssetWriter *fileAssetWriter; 17 | //video 输入 18 | @property (nonatomic, strong) AVAssetWriterInput *videoAssetWriterInput; 19 | //audio 输入 20 | @property (nonatomic, strong) AVAssetWriterInput *audioAssetWriterInput; 21 | 22 | @property (nonatomic, unsafe_unretained) BOOL isWriting; 23 | 24 | @property (nonatomic, copy) NSString *currentFilePath; 25 | 26 | //一个文件中写入多少个samples 27 | @property (nonatomic, unsafe_unretained) NSInteger samplesCountInEveryFile; 28 | 29 | @property (nonatomic, unsafe_unretained) NSInteger samplesCountCurrentFile; 30 | 31 | //已经完成的文件列表 32 | @property (nonatomic, strong) AWVideoFifoList *completedFileList; 33 | 34 | @property (nonatomic, strong) AWVideoFlvStream *flvStream; 35 | 36 | @end 37 | 38 | @implementation AWVideoMp4File 39 | //视频属性 40 | -(NSInteger)vWidth{ 41 | if (_vWidth == 0) { 42 | _vWidth = 320; 43 | } 44 | return _vWidth; 45 | } 46 | 47 | -(NSInteger)vHeight{ 48 | if (_vHeight == 0) { 49 | _vHeight = 576; 50 | } 51 | return _vHeight; 52 | } 53 | 54 | -(NSString *)vCodec{ 55 | if (_vCodec == 0) { 56 | _vCodec = AVVideoCodecH264; 57 | } 58 | return _vCodec; 59 | } 60 | 61 | -(NSInteger)vBitRate{ 62 | if (_vBitRate == 0) { 63 | _vBitRate = 600000; 64 | } 65 | return _vBitRate; 66 | } 67 | 68 | -(NSInteger)vMaxFrames{ 69 | if (_vMaxFrames == 0) { 70 | _vMaxFrames = 100; 71 | } 72 | return _vMaxFrames; 73 | } 74 | 75 | //音频属性 76 | -(NSInteger)aCodec{ 77 | if (_aCodec == 0) { 78 | _aCodec = kAudioFormatMPEG4AAC; 79 | } 80 | return _aCodec; 81 | } 82 | 83 | -(NSInteger)aBitRate{ 84 | if (_aBitRate == 0) { 85 | _aBitRate = 256000; 86 | } 87 | return _aBitRate; 88 | } 89 | 90 | -(CGFloat)aSampleRate{ 91 | if (_aSampleRate == 0) { 92 | _aSampleRate = 44100; 93 | } 94 | return _aSampleRate; 95 | } 96 | 97 | -(NSInteger)aChanlesNum{ 98 | if (_aChanlesNum == 0) { 99 | _aChanlesNum = 1; 100 | } 101 | return _aChanlesNum; 102 | } 103 | 104 | -(NSData *)aChannelLayoutData{ 105 | if (!_aChannelLayoutData) { 106 | _aChannelLayoutData = [NSData data]; 107 | } 108 | return _aChannelLayoutData; 109 | } 110 | 111 | -(BOOL)isWriting{ 112 | return _isWriting && _fileAssetWriter && _fileAssetWriter.status == AVAssetWriterStatusWriting; 113 | } 114 | 115 | -(NSInteger)samplesCountInEveryFile{ 116 | //这个数值比较重要,因为浮点数的原因,所以取音视频刚好是整数时间的值。 117 | //因为视频刚好是0.033333的帧率,所以0.033333*60刚好是2,这就保证视频不会卡顿 118 | //音频的话,音频帧率为0.023220 119 | if (_samplesCountInEveryFile == 0) { 120 | _samplesCountInEveryFile = 60; 121 | } 122 | return _samplesCountInEveryFile; 123 | } 124 | 125 | -(AWVideoFifoList *)completedFileList{ 126 | if (!_completedFileList) { 127 | _completedFileList = [AWVideoFifoList new]; 128 | } 129 | return _completedFileList; 130 | } 131 | 132 | -(AWVideoFlvStream *)flvStream{ 133 | if (!_flvStream) { 134 | _flvStream = [AWVideoFlvStream new]; 135 | } 136 | return _flvStream; 137 | } 138 | 139 | -(void) createAssetWriterWithFileName:(NSString *)path{ 140 | NSError *error = nil; 141 | 142 | if([[NSFileManager defaultManager] fileExistsAtPath:path]){ 143 | [[NSFileManager defaultManager]removeItemAtPath:path error:&error]; 144 | } 145 | 146 | self.currentFilePath = path; 147 | 148 | _fileAssetWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path isDirectory:NO] fileType:AVFileTypeMPEG4 error:&error]; 149 | 150 | if (!_videoAssetWriterInput) { 151 | _videoAssetWriterInput = 152 | [AVAssetWriterInput assetWriterInputWithMediaType: AVMediaTypeVideo 153 | outputSettings:@{ 154 | AVVideoCodecKey:self.vCodec, 155 | AVVideoWidthKey:@(self.vWidth), 156 | AVVideoHeightKey:@(self.vHeight), 157 | AVVideoCompressionPropertiesKey:@{ 158 | AVVideoAverageBitRateKey: @(self.vBitRate), 159 | AVVideoMaxKeyFrameIntervalKey:@(self.vMaxFrames) 160 | } 161 | }]; 162 | _videoAssetWriterInput.expectsMediaDataInRealTime = YES; 163 | } 164 | 165 | if ([_fileAssetWriter canAddInput:_videoAssetWriterInput]) { 166 | [_fileAssetWriter addInput:_videoAssetWriterInput]; 167 | } 168 | 169 | if (!_audioAssetWriterInput) { 170 | _audioAssetWriterInput = 171 | [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio 172 | outputSettings:@{ 173 | AVFormatIDKey: @(self.aCodec), 174 | AVSampleRateKey: @(self.aSampleRate), 175 | AVEncoderBitRatePerChannelKey:@(self.aBitRate), 176 | AVNumberOfChannelsKey: @(self.aChanlesNum), 177 | AVChannelLayoutKey:self.aChannelLayoutData 178 | }]; 179 | _audioAssetWriterInput.expectsMediaDataInRealTime = YES; 180 | } 181 | 182 | if ([_fileAssetWriter canAddInput:_audioAssetWriterInput]) { 183 | [_fileAssetWriter addInput:_audioAssetWriterInput]; 184 | } 185 | } 186 | 187 | -(AVAssetWriter *)fileAssetWriter{ 188 | if (!_fileAssetWriter) { 189 | [self createAssetWriterWithFileName: self.randomPath]; 190 | } 191 | return _fileAssetWriter; 192 | } 193 | 194 | -(BOOL) startWritingWithTime:(CMTime) time{ 195 | if (self.isWriting) { 196 | return NO; 197 | } 198 | if (self.fileAssetWriter.status == AVAssetWriterStatusUnknown) { 199 | if([self.fileAssetWriter startWriting]){ 200 | if (0 == CMTimeCompare(time, kCMTimeInvalid)) { 201 | time = kCMTimeZero; 202 | } 203 | [self.fileAssetWriter startSessionAtSourceTime:time]; 204 | }else{ 205 | return NO; 206 | } 207 | }else if(self.fileAssetWriter.status != AVAssetWriterStatusWriting){ 208 | return NO; 209 | } 210 | 211 | self.isWriting = YES; 212 | 213 | [self.flvStream startStream]; 214 | 215 | return YES; 216 | } 217 | 218 | -(void) completeOneFile:(void(^)())finishHandler{ 219 | [self.audioAssetWriterInput markAsFinished]; 220 | [self.videoAssetWriterInput markAsFinished]; 221 | [self.fileAssetWriter finishWritingWithCompletionHandler:finishHandler]; 222 | 223 | // samples count current file set to 0 224 | self.samplesCountCurrentFile = 0; 225 | // add current file to completed File List 226 | [self.completedFileList addElement:self.currentFilePath]; 227 | } 228 | 229 | -(void) stopWritingWithFinishHandler:(void(^)())finishHandler{ 230 | if (!self.isWriting) { 231 | return; 232 | } 233 | 234 | @try{ 235 | [self completeOneFile:finishHandler]; 236 | }@catch(id exception){ 237 | NSLog(@"complete one file exception %@", exception); 238 | } 239 | 240 | self.fileAssetWriter = nil; 241 | self.isWriting = NO; 242 | 243 | [self.flvStream stopStream]; 244 | self.flvStream = nil; 245 | } 246 | 247 | -(BOOL) writeAudioSample:(CMSampleBufferRef) audioSample{ 248 | if (self.isWriting && self.audioAssetWriterInput.isReadyForMoreMediaData) { 249 | // NSLog(@"write one audio sample to file"); 250 | @try{ 251 | [self.audioAssetWriterInput appendSampleBuffer:audioSample]; 252 | return YES; 253 | }@catch(NSException *exception){ 254 | NSLog(@"write audioSample exception: %@", exception); 255 | } 256 | }else{ 257 | NSLog(@"[ERROR] writeAudioSample failed! writing=%d isReady=%d", self.isWriting, self.audioAssetWriterInput.readyForMoreMediaData); 258 | } 259 | return NO; 260 | } 261 | 262 | -(void) savePathToLibrary:(NSString *)path{ 263 | NSURL *url = [NSURL fileURLWithPath:path]; 264 | NSFileManager *fileManager = [NSFileManager defaultManager]; 265 | 266 | if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { 267 | ALAssetsLibrary *assetsLib = [ALAssetsLibrary new]; 268 | [assetsLib writeVideoAtPathToSavedPhotosAlbum:url completionBlock:^(NSURL *assetURL, NSError *error) { 269 | NSLog(@"save file path=%@ assetUrl=%@ error=%@", path, assetURL, error); 270 | [fileManager removeItemAtPath:path error:nil]; 271 | }]; 272 | }else{ 273 | NSLog(@"没有找到文件!%@", path); 274 | } 275 | } 276 | 277 | -(BOOL) writeVideoSample:(CMSampleBufferRef) videoSample{ 278 | if (self.isWriting && self.videoAssetWriterInput.isReadyForMoreMediaData) { 279 | // NSLog(@"write one video sample to file"); 280 | @try{ 281 | [self.videoAssetWriterInput appendSampleBuffer:videoSample]; 282 | if (++self.samplesCountCurrentFile >= self.samplesCountInEveryFile) { 283 | [self newFileAssetWriter:^{ 284 | NSLog(@"完成一个文件:self.completedFileList=%@", self.completedFileList); 285 | [self.flvStream notifyMp4FileListChanged:self.completedFileList]; 286 | }]; 287 | } 288 | return YES; 289 | }@catch(NSException *exception){ 290 | NSLog(@"write videoSample exception: %@", exception); 291 | } 292 | }else{ 293 | NSLog(@"[ERROR] writeVideoSample failed! writing=%d isReady=%d", self.isWriting, self.videoAssetWriterInput.readyForMoreMediaData); 294 | } 295 | return NO; 296 | } 297 | 298 | -(NSString *)defaultPath{ 299 | return [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.mp4"]; 300 | } 301 | 302 | -(NSString *)randomPath{ 303 | return [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSUUID UUID].UUIDString stringByAppendingString:@".mp4"]]; 304 | } 305 | 306 | -(void) newFileAssetWriter:(void (^)())preFileFinishHandler{ 307 | [self completeOneFile:preFileFinishHandler]; 308 | [self newFileAssetWriterWithOutSaveFile]; 309 | } 310 | 311 | -(void) newFileAssetWriterWithOutSaveFile{ 312 | self.fileAssetWriter = nil; 313 | [self fileAssetWriter]; 314 | } 315 | 316 | -(void)clean{ 317 | [self completeOneFile:nil]; 318 | self.fileAssetWriter = nil; 319 | //移除所有文件 320 | while(!self.completedFileList.empty) { 321 | NSString *mp4File = self.completedFileList.popElement; 322 | [[NSFileManager defaultManager] removeItemAtPath:mp4File error:nil]; 323 | } 324 | } 325 | @end 326 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 26/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 26/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /iOS/pushStreamInC/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 26/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 26/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import 11 | #import 12 | 13 | #import "AWVideoCapture.h" 14 | 15 | @interface ViewController () 16 | //按钮 17 | @property (nonatomic, strong) UIButton *startBtn; 18 | @property (nonatomic, strong) UIButton *switchBtn; 19 | @property (nonatomic, strong) UIButton *saveVideoBtn; 20 | 21 | @property (nonatomic, strong) UIView *preview; 22 | @property (nonatomic, strong) AWVideoCapture *capture; 23 | @end 24 | 25 | @implementation ViewController 26 | 27 | -(void)videoCapture:(AWVideoCapture *)capture stateChangeFrom:(aw_rtmp_state)fromState toState:(aw_rtmp_state)toState{ 28 | switch (toState) { 29 | case aw_rtmp_state_idle: { 30 | self.startBtn.enabled = YES; 31 | [self.startBtn setTitle:@"开始录制" forState:UIControlStateNormal]; 32 | break; 33 | } 34 | case aw_rtmp_state_connecting: { 35 | self.startBtn.enabled = NO; 36 | [self.startBtn setTitle:@"连接中" forState:UIControlStateNormal]; 37 | break; 38 | } 39 | case aw_rtmp_state_opened: { 40 | self.startBtn.enabled = YES; 41 | break; 42 | } 43 | case aw_rtmp_state_closed: { 44 | self.startBtn.enabled = YES; 45 | break; 46 | } 47 | case aw_rtmp_state_error_write: { 48 | break; 49 | } 50 | case aw_rtmp_state_error_open: { 51 | break; 52 | } 53 | case aw_rtmp_state_error_net: { 54 | break; 55 | } 56 | } 57 | } 58 | 59 | - (void)viewDidLoad { 60 | [super viewDidLoad]; 61 | [self.preview addSubview: self.capture.preview]; 62 | self.capture.preview.center = self.preview.center; 63 | [self createUI]; 64 | [self test]; 65 | } 66 | 67 | -(AWVideoCapture *) capture{ 68 | if (!_capture) { 69 | _capture = [AWVideoCapture new]; 70 | _capture.delegate = self; 71 | } 72 | return _capture; 73 | } 74 | 75 | -(UIView *)preview{ 76 | if (!_preview) { 77 | _preview = [UIView new]; 78 | _preview.frame = self.view.bounds; 79 | [self.view addSubview:_preview]; 80 | [self.view sendSubviewToBack:_preview]; 81 | } 82 | return _preview; 83 | } 84 | 85 | -(void) test{ 86 | } 87 | 88 | -(void) createUI{ 89 | self.startBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 30)]; 90 | [self.startBtn setTitle:@"开始录制!" forState:UIControlStateNormal]; 91 | [self.startBtn addTarget:self action:@selector(onStartClick) forControlEvents:UIControlEventTouchUpInside]; 92 | [self.view addSubview:self.startBtn]; 93 | 94 | self.switchBtn = [[UIButton alloc] initWithFrame:CGRectMake(230, 100, 100, 30)]; 95 | [self.switchBtn setTitle:@"换摄像头!" forState:UIControlStateNormal]; 96 | [self.switchBtn addTarget:self action:@selector(onSwitchClick) forControlEvents:UIControlEventTouchUpInside]; 97 | [self.view addSubview:self.switchBtn]; 98 | 99 | self.saveVideoBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 150, 150, 30)]; 100 | [self.saveVideoBtn setTitle:@"保存到相册!" forState:UIControlStateNormal]; 101 | [self.saveVideoBtn addTarget:self action:@selector(onSaveVideo) forControlEvents:UIControlEventTouchUpInside]; 102 | [self.view addSubview:self.saveVideoBtn]; 103 | } 104 | 105 | -(void) onStartClick{ 106 | if (self.capture.isCapturing) { 107 | [self.startBtn setTitle:@"开始录制!" forState:UIControlStateNormal]; 108 | [self.capture stopCapture]; 109 | }else{ 110 | self.capture.rtmpUrl = @"rtmp://192.168.1.124/live/test"; 111 | if ([self.capture startCapture]) { 112 | [self.startBtn setTitle:@"停止录制!" forState:UIControlStateNormal]; 113 | } 114 | } 115 | } 116 | 117 | -(void) onSwitchClick{ 118 | [self.capture switchCamera]; 119 | } 120 | 121 | -(void) savePathToLibrary:(NSString *)path{ 122 | NSURL *url = [NSURL fileURLWithPath:path]; 123 | NSFileManager *fileManager = [NSFileManager defaultManager]; 124 | 125 | if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { 126 | ALAssetsLibrary *assetsLib = [ALAssetsLibrary new]; 127 | [assetsLib writeVideoAtPathToSavedPhotosAlbum:url completionBlock:^(NSURL *assetURL, NSError *error) { 128 | NSLog(@"save file path=%@ assetUrl=%@ error=%@", path, assetURL, error); 129 | [fileManager removeItemAtPath:path error:nil]; 130 | }]; 131 | }else{ 132 | NSLog(@"没有找到文件!%@",path); 133 | } 134 | } 135 | 136 | -(void) onSaveVideo{ 137 | [self savePathToLibrary:self.capture.videoMp4File.defaultPath]; 138 | } 139 | 140 | - (void)didReceiveMemoryWarning { 141 | [super didReceiveMemoryWarning]; 142 | // Dispose of any resources that can be recreated. 143 | } 144 | 145 | 146 | @end 147 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // pushStreamInC 4 | // 5 | // Created by kaso on 26/5/16. 6 | // Copyright © 2016年 xcyo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iOS/pushStreamInC/test.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hardman/ios_push_stream_with_mp4_hard_encode/8bd2e0214e6bfaab95156f4f6f817d8b1ff62b36/iOS/pushStreamInC/test.mov --------------------------------------------------------------------------------